前言
凭什么后来者居上?凭什么她的心只有我不能触摸?(o(╥﹏╥)o)
让我从JavaScript中的作用域、作用域链和预编译开始聊聊,相信你一定能从中有收获的
什么是的作用域?
简单来说就是能被访问的区域,在JavaScript中分为三种作用域,全局域、函数域和块级作用域,这里就不做细说了,可参考我的上一篇文章《浅谈JavaScript中的作用域》
什么是作用域链?
简单来时说就是一个一个作用域连起来,成了一条链子,有点抽象,看看图吧
这个关系可以一直套娃的,小的可以访问大的,大得不能访问小的,就好比在你心里深处的她,她已经参透你,拿捏你,可你却完全看不懂她
什么?你说我怎么在最里面?我劝你最好别问,怕你难过
来点专业点的解释
作用域链是一个由多个变量对象组成的链条,在引擎进行预编译的时候,会把当前作用域内的函数声明和变量声明等信息收集起来,形成一个变量对象。之后再将这个变量对象与父作用域的变量对象链接起来,不断重复,最终就构成了作用域链
什么是预编译?
简单来说就是,代码在执行前需要进行编译操作,用于确定代码之间的各种关联
情人节送礼物不得先看看你和她的关系?
别看了,你不在她心里,你收不到礼物的!!!
什么?你说为什么你送给她的礼物在我手里?那我只能说你不懂作用域链
不懂没事,继续往下,我给你捋捋
关系
通过一个一个作用域的关联,形成作用域链
为什么作用域能够清晰划分呢?他里面的变量凭什么我就不能访问?
预编译就是为了帮助作用域链去进行变量查找
现在知道为什么我收到了你的礼物了吧,情人节我和她要,她可不就和你要了?
预编译过程(重点)
每个函数都有一个内部属性[[scope]]用于存储函数中的有效标识符
在编译时
- 变量声明,声明提升 如:var a = 10 中声明提升 var a 后面才是a=10 而不是整体提升var a = 10
- 函数声明,整体提升
在全局和函数体内流程有所不同
-
全局
- 创建GO对象
- 找变量声明,将变量名作为GO的属性名,值为undefined
- 在全局找函数声明,将函数名作为GO的属性名,值为该函数体
-
函数体内
- 创建一个AO对象
- 找形参和变量声明,将形参和变量名作为AO的属性名,值为undefined
- 形参和实参统一
- 在函数体内找函数声明,将函数名作为AO的属性名,值为该函数体
这段代码如何编译的?
function test() {
}
函数调用,在引擎中创建这个函数的执行上下文对象,称之为AO action object,记录有效标识符,执行完之后会被回收掉
那么这段代码会如何编译呢?
function a() {
function b() {
b = 22;
}
var a = 111;
b();
console.log(a);
}
var glob = 100;
a();
我们来分析一下
首先会创建GO,GO里面存放着变量、函数,并且变量的值为undefined
创建完毕后,开始调用,调用a()后,创建AO对象,将形参和变量名作为AO的属性名,值为undefined,形参和实参统一,在函数体内找函数声明,将函数名作为AO的属性名,值为该函数体
其实这个就是该段就是函数a的作用域链
当执行a时又发现了b函数,于是就又给b创建作用域了,b的作用域首先肯定是指向a的作用域的,因为是a的执行带来了b的创建
后来执行b了,于是给b创建内部的作用域
接下来分析这段代码会输出什么?
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){}
console.log(a);
var b = function(){}
console.log(b);
function c(){}
var c = a
console.log(c);
}
fn(1);
第一次创建GO对象
GO{
fn:function
}
执行后创建a的oa对象
AO{
a:undefined 1 function
b:undefined
c:undefined function 123
}
所以执行后结果问为function 123 123 function 123
总结
相信看完本章你一定能明白(▽)
凭什么只有你不能触摸她的心?
为什么你送给她的礼物在我手里?