一、背景描述
写这篇博客的原因是今天看了一个小例子,我不懂,有一点不理解,它是这个样子的
function foo() {
console.log('1111111');
}
var foo = function() {
console.log('2222222');
}
foo();// 2222222
这个没啥不懂的,主要是然后给它换个顺序
var foo = function() {
console.log('2222222');
}
function foo() {
console.log('1111111');
}
foo();// 2222222
这两个的结果都是2222222,我就有点不太理解了。
二、相关知识点
为了搞明白是怎么肥四,我去研究了一下红宝书,我发现我是知道执行上下文、执行过程和声明提升的概念的,我看不懂前面那个栗子的原因是因为我没有分清楚函数声明和函数表达式。
我先把涉及到的知识点列举一下
(一)执行上下文
执行上下文就是当前代码的执行环境/作用域,但是它又不等同于作用域,可以说,执行上下文包含了作用域。
执行上下文包括变量对象、作用域链及this的指向
(二)代码执行的两个阶段
执行JavaScript主要分为以下两个阶段
1、代码预编译阶段
预编译阶段是前置阶段,这一阶段由编译器将JavaScript代码编译成可执行的代码。这是JavaScript语言里面一个非常独特的概念。
先进行语法分析,没有问题以后,在预编译阶段对JavaScript代码中变量的内存空间进行分配,变量提升就是在这个阶段完成的。
预编译阶段有几个细节:
(1)在预编译阶段进行变量声明;
(2)在预编译阶段对变量声明进行提升,但是值为undefined;
(3)在预编译阶段对所有非表达式的函数声明进行提升。
2、代码执行阶段
执行阶段的主要任务就是执行代码逻辑,执行上下文在这个阶段会全部创建完成
作用域在预编译阶段确定的,但是作用域是在执行上下文的创建阶段完全生成的,因为函数在调用时才会开始创建对应的执行上下文。
(三)函数声明和函数表达式
这两个概念是有区别的,Javascript引擎在加载数据的时候对它们是区别对待的。
- JavaScript引擎在任何代码执行之前,也就是前面说的预编译阶段,会先读取函数声明。
然后在执行上下文中生成函数定义。 - 而函数表达式必须等到代码执行到它那一行,才会在执行上下文中生成函数定义。
我的点就是,我之前没有分清楚啥叫函数声明,啥叫函数表达式
这个是函数声明
function foo() {
console.log('1111111');
}
这个叫函数表达式
var foo = function() {
console.log('2222222');
}
函数声明是会提升的,JavaScript引擎会先执行一遍扫描,把发现的函数声明提升到源代码树的顶部,那前面这个栗子中
var foo = function() {
console.log('2222222');
}
function foo() {
console.log('1111111');
}
foo();// 2222222
就是说,预编译阶段,先把这段声明提升到最顶部了
function foo() {
console.log('1111111');
}
后面在执行阶段中,又用函数表达式,对这个foo赋值了,所以输出的结果才是函数表达式中的结果2222222。
终于把它搞明白啦啦啦啦~开心~
三、总结
总结一下,JavaScript引擎执行最基本的原理就是说
它像一条流水线。
第一道工序是预编译阶段,创建变量对象(Variable Object,VO),只创建不赋值
第二道工序是代码执行阶段,变量对象变成激活对象(Active Object,AO)。这个时候就确定了作用域链。
这也解释了为毛这个可以正确输出3
console.log(sum(1, 2));
function sum(num1, num2){
return num1 + num2;
}
但是这个却会保错
Uncaught ReferenceError: Cannot access 'sum' before initialization
console.log(sum(1, 2));
let sum = function(num1, num2) {
return num1 + num2;
}
这就是因为函数声明是会提升的,但是函数表达式不会,你学废了嘛?
点个赞吧~