目录
2、隐式操作==>预编译==> (形 [形参、变量] 实 函 运 )
一、预编译
1、分析代码是否正确、符号、词法分析等
2、隐式操作==>预编译==> (形 [形参、变量] 实 函 运 )
函数再调用之后,在运行代码之前会生成一个对象==> 执行期对象AO==> activation Object(执行期上下文)==>每次调用就会创建一个AO对象
形:将 函数内部的 局部变量 和 形参变量 添加到AO对象内,作为AO对象的属性名。但是属性值是undefined。==》变量和形参名提升
实:把传入的实参赋值给AO对象的属性(如果实参是变量,那应该先取值后再传入)
函:局部函数声明、赋值。把局部函数的名字让AO对象也有一个一样的成员名,把函数体赋值给这个属性。==>函数整体提升
3、运行代码:
运:运行代码。
函数运行完毕,AO就释放了
二、全局预编译
全局作用域运行代码时,也有预编译==> 全局预编译==> 形 [变量] 函 运
1、生成一个对象Global Object ( GO:{ } )
2、把所有的全局变量设置为GO的属性名
3、把所有函数名作为GO的成员名,把哈桑农户体赋值给这个成员
全局编译还有一步,不同的环境中JS代码不一样
如果是浏览器,还会执行异步GO给window对象共享成员
GO对象的成员全部浅拷贝给环境对象window (node.js 环境中没有这一步)
4、执行代码
拓展:关于访问成员:
console.log(a) 访问的是GO对象的成员,如果作用域链中都没有就报错
console.log(window.a) 原型链上如果都没有就返回undefined,不会报错
三、作用域链
closure:闭包
JS对象有两种成员:
1、上文成员(JS语法可以直接访问的成员)
2、下文成员(底层语法访问的成员)==> [[]]括起来的成员名,就是下文成员
函数的属性: length、name、[[scoped]] ==>
[[scoped]] ==> 这个“对象”内存保存的就是函数的作用域。可以通过console.dir(fn)看见。
函数在定义/声明的时候,就有了[[scoped]] (只能引擎使用),里面保存了上层的AO对象
==> 函数调用时会生成AO对象,AO保存在[[scoped]] 对象内部,每次调用都会放在scoped的上面,先放的就放在下面
每个函数scopes数组天生就有一个AO对象,就是这个函数的上层的AO
总结:
函数定义/声明 ==> 生成scopes,并将本来就有的GO/AO放入到scopes的最下面
函数调用 == > 生成AO
function fn(a) { function fm() { var b=20 console.log(a) } fm() } var c=2 fn(10) fn(20)
AO会被销毁