JavaScript——预编译 + 作用域链

117 阅读2分钟

一、预编译

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的成员名,把函数体赋值给这个成员

例如:

                        console.log(a)
			var a = 20
			console.log(a)
			function fn() {
				console.log(123)
			}

image.png

分析: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()

image.png

image.png

当fm中没有找到a变量时,就会去fm的上一层AO对象中找,也就是到fn的[[scopes]]属性中找;所以最后打印a为20