一、预编译
1.js中函数运行的三个过程:(1)词法分析 (2)预编译 (3)运行代码
2.预编译过程:
1.函数每次调用都会生成一个对象,叫做执行期上下文对象,也称为AO对象。
2.给AO对象添加成员: 函数内部的局部变量和形参变量名作为AO对象的属性名。
3.把传入的实参赋值给AO对象相对应的属性。
4.局部函数声明,赋值 把局部变量的名字让AO对象也有一个一样的成员,把函数体赋值给这个属性。
例如:
function fm(a) {
console.log(a)
var a = 20
console.log(a)
}
fm(100)
分析:1.fm(100)调用函数时生成一个AO对象:AO:{ }
2.将函数的形参和内部的变量以及函数作为AO的属性 AO:{ a:undefined}
3.将传入的实参赋值给形参 AO: {a:100}
4.函数执行,又重新给a赋值为20 AO:{a:20 }
所以第一次打印100,第二次打印20
3.当全局作用域运行代码时 ,也有预编译
1.生成一个对象Global Object (GO对象)
2.把所有的全局变量 设置为GO的属性名
3.把所有的函数名作为GO的成员名,把函数体赋值给这个成员
4.看是不是浏览器环境中的js脚本,如果是浏览器 还会执行一步: GO给window对象共享成员
例如:
console.log(a)
var a = 20
console.log(a)
function fn() {
console.log(123)
}
分析:1.生成一个GO对象 GO:{ }
2.把所有的全局变量 设置为GO的属性名 GO: { a;undefined }
3.把所有的函数名作为GO的成员名,把函数体赋值给这个成员 GO :{a;undefined,function fn() {console.log(123)} }
4.执行代码,第一个打印undefined,然后将a重新赋值为20,所以第二个打印20
二、作用域链
1.函数在定义/声明时, 就有了[[scopes]] 属性,也就是作用域"数组"(只能引擎使用), 里面保存了上层的AO对象。
2.函数调用时会生成AO对象 会把AO对象放在[[scopes]]中,并且每次调用都会放在scopes前面(顶部)
3.当函数中还嵌套了其他函数时,内部函数没有某个变量时,就会去它上一层AO对象中找,于是就会形成一条链,就称为作用域链
例如:
function fn() {
var a = 20
function fm() {
var b = 30
console.log(a)
}
fm()
}
fn()
当fm中没有找到a变量时,就会去fm的上一层AO对象中找,也就是到fn的[[scopes]]属性中找;所以最后打印a为20