共计 1630 个字符,预计需要花费 5 分钟才能阅读完成。
凭什么后来者居上?凭什么她的心只有我不能触摸?(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
相信看完本章你一定能明白(^▽^)
凭什么只有你不能触摸她的心?
为什么你送给她的礼物在我手里?