文章目录
前言
本文主要介绍执行上下文、作用域以及作用域链的相关概念和知识
一、执行上下文
变量或函数的上下文决定了他们可以访问哪些数据以及行为。每个上下文都有一个关联的变量对象,而这个上下文钟定义的所有变量和函数都存在于这个对象上。
1. 作用域分类
- 全局上下文
全局上下文就是我们常说的window对象,全局变量和函数都会成为window对象的属性和方法 - 函数上下文
在函数执行的时候,会为这个函数创建函数执行上下文。
2. 代码分析
//1. 进入全局执行上下文
var a = 10
var b = function (x) {
var b = 5
c(x + b) //3. 进入c执行上下文
}
var c = function (y) {
var c = 5
console.log(a + c + y)
}
b(10) //2. 进入b函数执行上下文
2.1 全局上下文分析
- 代码执行前,首先会创建全局的执行上下文。
- 创建window变量对象,将全局变量和函数存到window中,并压入栈中。
- 在执行var a = 10之前所做的预解析工作,可以观察到在全局中所定义的变量和函数都保存在了window中,并且值为undefined。
- 全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器
2.2 函数上下文分析
-
在执行函数之前,创建函数执行上下文。
-
收集里面的所有变量和函数,保存在活动对象中(activation object)(如果是函数上下文,则活动对象用作变量对象),并压入栈中。
-
观察到 这个活动对象中不仅收集了x和b 还对this进行了初始化。
-
函数执行完毕后,将其从栈顶中弹出,将控制权返还给之前的执行上下文。
2.3 趁热打铁
依次输出什么
console.log('global begin: '+ i)
var i = 1
foo(1);
function foo(i) {
if (i == 4) {
return;
}
console.log('foo() begin:' + i);
foo(i + 1);
console.log('foo() end:' + i);
}
console.log('global end: ' + i)
结果:
global begin: undefined
foo() begin: 1
foo() begin: 2
foo() begin: 3
foo() end :3
foo() end:2
foo() end 1
global end 1
二、作用域
1. 什么是作用域
作用域为可访问变量,对象,函数的集合
2. 作用域分类
- 全局作用域
- 函数作用域
- eval作用域
- 块级作用域(ES6)
3. 作用域的作用
隔离变量,不同作用域下同名变量不会有冲突
三.作用域链
1. 什么是作用域链
上下文中的代码在执行的时候,会创建变量对象的一个作用域链(scope chain)。这个作用域链决定了各级上下文中的代码访问变量和函数的顺序。
2. 作用域链产生过程
var color = "blue";
function changeColor() {
let anotherColor = "red";
function swapColors() {
let tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 这里可以访问 color、anotherColor 和 tempColor
}
// 这里可以访问 color 和 anotherColor,但访问不到 tempColor
swapColors();
}
// 这里只能访问 color
changeColor();
- 在changeColor()这个函数定义的时候,就为其添加了一个[[Scopes]] 这个属性,其保存上一级的作用域链。在这个例子中上一级的作用域链就是全局作用域。
- 在函数执行的时候,进入执行上下文环境,将这个函数的[[Scopes]]复制作为作用域链。
- 创建活动对象,并将活动对象添加到最前端。那么这个就是当前函数的作用域链。
注意图中的Closure是闭包的意思。
下图是这个例子的作用域链的直观表示。
3. 趁热打铁
练习1
var x = 10;
function fn() {
console.log(x);
}
function show(f) {
var x = 20;
f();
}
show(fn);
打印结果为:10
分析思路:主要是看fn的作用域链,其作用域链是中第一个是其活动对象,由于里面不包含去一下一个作用域查找也就是全局作用域了),
练习2
var fn = function () {
console.log(fn)
}
fn()
var obj = {
fn2: function () {
console.log(fn2)
}
}
obj.fn2()
结果:ƒ () {console.log(fn) }
Uncaught ReferenceError: fn2 is not defined
分析:首先是fn,最后在全局作用域中找到了fn并打印。为什么fn2却没打印呢? 这是因为fn2()作用域链上只有它自己的活动对象(因为fn2在对象内部,而不是全局作用域中,没有被预解析到),而它活动对象里面没有fn2,最后只能报错。