目录
作用域
作用域表示一个代码区域,也表示一个运行环境
1. 全局作用域
在全局作用域中声明的变量,会被提升到脚本块的顶部,并且会成为全局对象的属性。
2. 函数作用域
在函数作用域中声明的变量,会被提升到函数的顶部,并且不会成为全局对象的属性.
浏览器执行JavaScript的过程
1.语法分析
基本语法检测,有没有基本的语法错误,例如中文标点符号等
2.预编译(预解析)
1. 全局预编译:
① 全局执行代码之前,生成一个GO(Global Object)
②分析变量的声明,var a
如果GO对象上有没有a属性,如果没有,则直接添加a属性,值为undefined
如果GO对象上已经存在a属性,则不做任何操作
③分析函数声明,function fn
如果GO对象已经存在fn属性,直接将整个函数体覆盖,如果不存在就添加fn属性,值为函数体
2. 函数预编译:
①函数运行前的一瞬间,生成Active Object(活动对象),后面简称AO
②函数声明的参数(形参),形成AO对象的属性名,属性值为实参的值,未传递实参则为undefined
③分析变量声明,如 var a 如果AO对象上还没有a属性,则添加AO属性,值为undefined 如果AO对象上已经有a属性,则不做任何影响
④分析函数声明,如 function foo(){} 则把函数赋给foo属性 如果此前foo属性存在,则覆盖
3.逐行执行
浏览器按照预编译分析出来的AO,GO逐行执行代码
注:分析过了变量、函数声明的就不用管。后续只需要管赋值即可
案例分析1:
g = 100;
function fn(){
console.log(g);
g = 200;
console.log(g);
var g = 300;
}
fn();
var g;
console.log(g);
先创建GO,分析变量提升问题,此时GO里有:
GO{
g:undefined,
fn:function () {}
}
然后逐行执行代码:给g赋值:100;
GO{
g:100,
fn:function () {}
}
调用fn(),生成AO,分析变量提升
AO{
g:undefined
}
然后再逐行执行代码,函数内部第一行输出的g,先在自己的作用域里找,AO里面有g,此时为undefined所以输出undefined,然后给g赋值200,所以第二个输出g为200;然后给g赋值300;
GO{
g:100,
fn:function () {}
}
AO{
g:300
}
第三次输出g,是在函数外部输出,外部GO里g=100;所以输出100
[[scopes]]
它是函数的一个私有属性,GO,AO存储在其中;
函数在定义时,函数的scopes属性会自动存储GO;
函数在运行,scopes属性中会继续添加当前函数预编译后的AO,会以栈的形式存储,形成作用域链
当函数运行时,会创建一个新的叫做执行上下文的对象,这个对象中定义了一个函数执行时的环境(var、function、this、document)。函数每次执行都会创建一个独一无二的执行上下文,在函数执行完后,就会销毁掉它所创建的执行上下文;
查询变量时沿着作用域链自顶部向下查询,如果查询到作用域链的底部(GO),还没有找到变量,系统就会报错(变量 is not defined)
案例分析2:
function a(){
function b(){
var bb = 234;
c = 111; //c没有var关键字声明,会自动生成在全局作用域中(GO)
console.log(a);
console.log(c);
}
var a = 123;
b();
console.log(c);
}
var global = 100;
a();
console.log(c);
---------------
GO{
global:100
a:function(){}
c:111
}
//调用a,生成aAO;
aAO{
a:123,
b:function(){}
}
//调用b,生成bAO;
bAO{
bb:234,
}
按步骤分析完GO,a的AO,b的AO后
函数a定义后 a的scopes属性 ,存储了GO
GO |
函数a被执行的时候 a的scopes属性,添加自己的AO
aAO |
GO |
函数b定义后 b的scopes属性 ,存储了GO 和外部a函数的AO
aAO |
GO |
函数b被执行的时候 ,生成自己的AO 添加到 scopes
bAO |
aAO |
GO |
查询变量时沿着作用域链自顶部向下查询,如果查询到作用域链的底部(GO),还没有找到变量,系统就会报错(变量 is not defined)
所以依次输出:123,111,111,111