## 执行上下文
1. 理解:
- 执行上下文抽象的概念,代表了代码执行的环境,包含: 执行环境,变量对象,this,作用域链
2. 流程:
- js引擎在js代码正式执行之前会先创建一个执行环境(开发商批的地,工程队施工的环境)
- 进入该环境以后创建一个变量对象(打地基),该对象用于收集当前环境下的: 变量,函数,函数的参数,this
- 找关键字var ,function
- 确认this的指向
- 创建作用域链[[scopes]](此时的作用域链只有上一级而没自身,只有在函数被调用时才在[[scopes]]中加入自己
3. 重点:
- 执行上下文是动态创建的
- 尤其是针对函数,每调用一次函数都会创建一次执行上下文
- 执行上下文符合出栈和入栈。
练习题:
<script type="text/javascript">
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)
</script>
输出:
global begin: undefined
:20 foo() begin:1
:20 foo() begin:2
:20 foo() begin:3
:22 foo() end:3
:22 foo() end:2
:22 foo() end:1
:24 global end: 1
解析:
作用域
作用域理解
- 抽象的概念
- 用来决定代码执行的范围, 变量所属的范围
- 作用域是代码定义的时候决定的
- 作用域作用:
- 隔离变量
- 规定其之后的作用域链是什么样的,体现: [[scopes]]: 上一级作用域链
作用域链
- 作用域链式一个数组结构
- 该结构内保存的是一个个的变量对象
- 作用域链什么时候创建的:在js代码正式执行之前创建的
1. 理解
* 就是一块"地盘", 一个代码段所在的区域
* 它是静态的(相对于上下文对象), 在编写代码时就确定了
* 例子: 房地产开发商盖楼盘之前批的 ‘地盘’
2. 分类
* 全局作用域
* 函数作用域
* eval作用域
* 没有块作用域(ES6有了)
3. 作用
* 隔离变量,不同作用域下同名变量不会有冲突
* 规定当前函数之后创建作用域链的上一个链条是什么样的
作用域和执行上下文区别
1. 区别1
* 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
* 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
* 函数执行上下文环境是在调用函数时, 函数体代码执行之前创建
2. 区别2
* 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
* 上下文环境是动态的, 调用函数时创建, 函数调用结束时上下文环境就会被释放
3. 联系
* 上下文环境(对象)是从属于所在的作用域
* 全局上下文环境==>全局作用域
* 函数上下文环境==>对应的函数使用域
作用域链
1. 理解
* 多个上下级关系的作用域形成的链式关系, 它的方向是从下向上的(从内到外)
* 作用链是一个数组结构的数据,保存着当前作用域的变量对象及其上级作用域的变量对象,直到全局的变量对象
* 查找变量时就是沿着作用域链来查找的
2. 作用域链如何产生
* 函数在定义的时候自动添加一个属性 ‘[[Scopes]]’, 该属性保存的是其上级作用域链
* 当函数执行的时候,进入执行上下文环境,将创建的变量对象添加到‘[[Scopes]]’数组的第一个位置,形成新的数组,该数组就是当前函数拥有的作用域链
2. 查找一个变量的查找规则
* 先在当前作用域的变量对象中查找,如果有就使用
* 如果没有就会沿着作用域链的数组去上级作用域中的变量对象中查找
* 找到就返回对应的值,如果没有继续向上查找,直到找到最后一个变量对象(全局的变量对象),如果还没有就会报错
// 创建全局作用域 ---> 预解析工作 ---> 创建全局的执行上下文 ---> 执行上下文环境 ---> 全局变量对象{a: undefined, this: window, fun:function} ---> 作用域链[全局变量对象]
var a = 123;
var b = 'abc'
// 先定义---> 创建局部作用域---> 函数自身有一个[[scopes]]: 上一级作用域链(global)
function fun() {
// 创建局部执行上下文 ---> 局部变量对象{a: undefined, fun2: function, this: window} ---> 创建作用域链[局部变量对象, Global]
var a = 234;
var c = 345;
console.log(a);
console.log(b);
//未调用函数时
// fun2函数已经定义了 ---> 创建局部作用域(fun2)---> 函数自身有一个[[scopes]]: 上一级作用域链[fun的变量对象, Global]
function fun2() {
//调用函数时
// 创建局部执行上下文 ---> 局部变量对象{this: window} ---> 创建作用域链: [fun2的局部变量对象,fun的变量对象, Global] ————最后一步是插入自己的作用域,形成完整的作用域链
console.log(c);
console.log(d);
}
fun2();
}
fun();
练习题
var obj = {
fn2: function () {
console.log(fn2) // 报错
}
}
obj.fn2()
解析:执行上下文时只寻找变量,函数,函数的参数,this,而var obj定义的函数 ,在预解析时被当作变量而非函数。所以当console.log(fin2)寻找上一级变量时是Global ,所以没有fn2这个函数,所以报错。
作用域链和原型链区别: