lgwebdream / FE-Interview

🔥🔥🔥 前端面试,独有前端面试题详解,前端面试刷题必备,1000+前端面试真题,Html、Css、JavaScript、Vue、React、Node、TypeScript、Webpack、算法、网络与安全、浏览器
https://lgwebdream.github.io/FE-Interview/
Other
6.77k stars 897 forks source link

Day291:说一下 JavaScript 的执行流程 #1112

Open Genzhen opened 3 years ago

Genzhen commented 3 years ago

每日一题会在下午四点在交流群集中讨论,五点小程序中更新答案 欢迎大家在下方发表自己的优质见解

二维码加载失败可点击 小程序二维码

扫描下方二维码,收藏关注,及时获取答案以及详细解析,同时可解锁800+道前端面试题。


前言

我们来看下面这一段代码。

print();
console.log(str);
var str = "hello world!";
function print() {
  console.log(str);
}

如果你写过 js,那就知道,这段代码不会报错,会正常输出 undefined undefined

如果不能理解,不要着急,我们删除 var str = 'hello world',再执行:

print();
console.log(str);
function print() {
  console.log(str);
}

现在就会报错了,str is not defined。这是为什么呢?如果不知道不要紧,下边我们就来了解下,要了解 JavaScript 的运行方式,我们就要先了解下变量提升。

变量提升(Hosting)

在介绍变量提升之前,我们先通过以下两个例子来讲解 JS 中的声明和赋值。

在 es6 之前,JS 都是通过 var 来声明变量。

var str = "hello word";

这一句等价于

var str; // 变量声明
str = "hello world!"; // 赋值

下面这段代码是一个完整的函数声明,没毛病。

var print = function () {
  console.log(str);
};

这段代码是一个声明变量和赋值的过程,相当于

var print; // 变量声明
print = function () {
  // 赋值
  console.log(str);
};

所谓的变量提升,是指 JavaScript 在执行的过程中,JavaScript 引擎把变量提升和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置值为 undefined。

JavaScript 执行流程

从概念的字面上来看,“变量提升”意味着变量和函数的声明会在物理层移动到代码的最前面。但其实是:变量和函数声明在代码里的位置是不会改变的,而且在编译阶段被 JavaScript 引擎放入内存中。JS 代码编译完成之后才会进入执行阶段。

大致流程就是:一段 JavaScript 代码 => 编译阶段 => 执行阶段

下面来看下详细的流程,还是以上边的例子为例

var str = undefined;
function print() {
  console.log(str);
}
print();
console.log(str);
str = "hello world";

执行流程图如下:

img

从上图可以看出,输入一段 JS 代码,经过编译后,会生成两部分内容:执行上下文(Execution context)和可执行代码。

执行上下文

是指 JS 执行一段代码时的运行环境,比如调用一个函数,就会进入这个函数的执行上下文,确定该函数在执行期间的用到变量如 this、变量、对象、函数等。

在执行上下文中存在一个变量环境的对象(Variable Environment),该对象中保存了变量提升的内容,比如上面代码中变量 str 和函数 print 都保存在该对象中。

类似这样:

Variable Environment:
    str -> undefined,
    print -> function () { console.log(str) };

接下来逐行分析下代码:

print(); // 1
console.log(str); // 2
var str = "hello world!"; // 3
function print() {
  // 4
  console.log(str);
}

编译阶段

这样就生成了变量环境对象,接着 JS 引擎会把声明之外的代码编译成字节码(可执行代码),也就是下面这一段模拟代码:

print();
console.log(str);
str = "hello world!";

执行阶段

JS 引擎开始执行“可执行代码”,按照顺序一行一行的执行,过程如下:

Variable Environment:
    str -> 'hello world!',
    print -> function () { console.log(str) };

整个流程大致差不多这样。

有个问题,如果代码中存在多个相同的变量和函数时怎么办?

示例代码:

a(); // 1
var a = "hello world"; //2
console.log(a); // 3
function a() {
  // 4
  console.log("inner a function 1");
}

var a = "hello, tomorrow"; // 5

console.log(a); // 6
function a() {
  // 7
  console.log("inner a function 2");
}
a(); // 8

执行结果是:

/* 
inner a function 2
hello world
hello, tomorrow

报错 a is not a function
*/

分析下:

编译阶段

Variable Environent:
    a -> undefined;
Variable Environent:
    a -> function () { console.log ('inner a function 1')};
Variable Environent:
    a -> function () { console.log ('inner a function 2')};

然后执行可执行代码:

a(); // 1
a = "hello world"; //2
console.log(a); // 3
a = "hello, tomorrow"; // 4
console.log(a); // 5
a(); // 6
inner a function 2
Variable Environent:
    a -> 'hello world';
hello world
Variable Environent:
    a -> 'hello, tomorrow';
hello,tomorrow
Humilitas commented 3 years ago

最后面漏了一点吧,应该是“5.打印 a 的值,输出 'hello, tomorrow'。6.函数调用,但是……”

zhuibo66 commented 3 years ago

问下,如果代码中存在多个相同的变量和函数时怎么办?这题的,1处,是不是不是这样的,我拿chrome测试了下,1这里的,a的值直接指向了=function a() { // 7 console.log("inner a function 2"); }