变量提升
- 用var 定义的变量会提升到最顶部,所以可以使用这个变量前声明或者使用后声明
- const 和let 一定要先声明才能使用
- 函数声明也会提升到最顶部
function a() {} // 这个是函数声明
var a = function () {} 这个是声明了一个变量a,给a赋值一个函数体,不是函数声明!!!
JS 代码运行的3大步骤
- 词法分析:顾名思义就是检查一遍js代码内有没有出现语法错误(比如少写分好,多写括号等);词法分析阶段不执行代码。
- 预编译:这个阶段发生在代码执行的前一刻,这个过程就是在内存里创建一个空间,用来存放你定义的变量和函数
- 解释执行:执行代码
GO和 AO
- JS 在执行前会产生一个GO(global object),也就是全局作用域
- 当一个方法被调用时会形成一个局部作用域AO(activation object)
预编译的执行步骤
暗示全局变量(imply global)
- 学习预编译前要知道的事情:任何变量如果未经声明就赋值,那么此变量就为全局对象所有。
GO对象
- 创建GO对象
- 寻找变量声明,值设定为undefined。
- 寻找函数中的函数声明,将函数名作为GO属性名,值为函数体。
AO对象
- 创建AO对象
- 寻找函数的形参和变量声明,将变量和形参名作为AO对象的属性名,值设定为undefined。
- 将形参和实参相统一,即更改形参后的undefined为具体的形参值。
- 寻找函数中的函数声明,将函数名作为AO属性名,值为函数体。
至此,预编译环节结束,函数中变量按照AO对象中的值开始执行
举几个🌰
eg1
function fn(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {};
console.log(a);
var b = function () {};
console.log(b);
function d() {};
}
fn(1);
以下都是伪代码,是我们肉眼看不见的
第一步: 词法分析有没有语法错误
第二步: 开始预编译
1、先创建一个全局对象GO
GO:{}
2、寻找变量声明,值设定为undefined,这个例子里面没有变量声明
GO:{}
3、寻找函数中的函数声明,将函数名作为GO属性名,值为函数体
GO:{
fn:function // 我们用function来代替函数体
}
4、GO寻找完了之后,开始寻找AO,先创建一个AO
AO:{}
5、寻找函数的形参和变量声明,将变量和形参名作为AO对象的属性名,值设定为undefined。用var声明的变量都是AO的属性名
AO:{
a: undefined, // 形参
b: undefined, // 用var声明的变量,虽然给它赋值的是函数,但是它还是变量,不属于函数声明
}
6、将形参和实参相统一,即更改形参后的undefined为具体的形参值。
AO:{
a: 1, // 形参和实参相统一,fn(1),这里的实参是1
b: undefined, // 用var声明的变量,虽然给它赋值的是函数,但是它还是变量,不属于函数声明
}
7、寻找函数中的函数声明,将函数名作为AO属性名,值为函数体。
AO:{
a: function, // 发现了函数声明 function a() {};
b: undefined, // 用var声明的变量,虽然给它赋值的是函数,但是它还是变量,不属于函数声明
d:function,// 发现了函数声明 function d() {};
}
这个时候其实相当于fn函数里面代码是这个样子的:
function fn(a) {
function a() {}; // 函数提升到上面
function d() {}; // 函数提升到上面
console.log(a);
var a = 123;
console.log(a);
console.log(a);
var b = function () {}; // 这个是变量声明,不是函数声明
console.log(b);
}
第三步: 代码解释执行
1、遇到第一个console.log(a); 这个时候看看AO里面的a的值是什么
AO:{
a: function, // 发现了函数声明 function a() {};
b: undefined, // 用var声明的变量,虽然给它赋值的是函数,但是它还是变量,不属于函数声明
d:function,// 发现了函数声明 function d() {};
}
可以执行输出function a() {}
2、下一行var a = 123,给a赋值了,所以AO里面进行修改
AO:{
a: 123, // 给a赋值123
b: undefined, // 用var声明的变量,虽然给它赋值的是函数,但是它还是变量,不属于函数声明
d:function,// 发现了函数声明 function d() {};
}
3、又遇到了console.log(a);
AO:{
a: 123, // 给a赋值123
b: undefined, // 用var声明的变量,虽然给它赋值的是函数,但是它还是变量,不属于函数声明
d:function,// 发现了函数声明 function d() {};
}
这个时候看AO里面a的值已经是123了,所以输出123
4、下一行有function a() {}; 这个函数声明已经在预编译阶段就提升了,所以看下一行。
5、再次遇到了console.log(a);
AO:{
a: 123, // 给a赋值123
b: undefined, // 用var声明的变量,虽然给它赋值的是函数,但是它还是变量,不属于函数声明
d:function,// 发现了函数声明 function d() {};
}
这个时候没有对a作任何改变,所以依然输出123
6、 var b = function () {}; 这行对b做了一个赋值,赋值给它一个函数体,所以我们对AO进行修改
AO:{
a: 123, // 给a赋值123
b: function, // 赋值函数体 function () {}
d:function,// 发现了函数声明 function d() {};
}
7、console.log(b); 这个时候看看AO里面的b的值是什么
AO:{
a: 123, // 给a赋值123
b: function, // 赋值函数体 function () {}
d:function,// 发现了函数声明 function d() {};
}
可以看到是function,所以输出function () {}
后面几个例子可以自己试试,套用上面的步骤来操作
eg2
function test(a,b){
console.log(a);
c = 0;
var c;
a = 3;
b = 2;
console.log(b);
function b() {};
function d() {};
console.log(c);
}
test(1);
eg3
console.log(fun);
function fun(fun){
console.log(fun);
var fun = 234;
console.log(fun);
function fun () {};
}
fun(1);
var fun = 123
eg4
function demo(){
console.log(bb);
if (aa) {
var bb = 100;
}
console.log(bb);
cc = 234
console.log(cc);
}
var aa;
demo();
aa = 10;
console.log(cc);
eg5
a = 100;
function eg(e){
function a() {};
arguments[0] = 2;
console.log(e);
if (a) {
var b = 123;
var c = function(){}
}
var c;
a = 10;
var a;
console.log(b);
f = 123;
console.log(c);
console.log(a);
}
var a;
eg(1);
console.log(a);
console.log(f);
公众号
欢迎大家关注我的公众号: 石马上coding,一起成长