面试系列原地址 gitee.com/gusiil/fein…,持续更新中
执行上下文
执行上下文,即代码的执行环境,执行上下文有三个重要的属性
- 变量对象
- 作用域链
- this
有两个重要的阶段:
- 进入执行上下文
- 代码执行
属性
变量对象
- 全局对象
- 函数对象
变量提升和函数提升:
在进入执行上下文时候,还没执行代码时,会进行以下三个声明步骤
- 形参声明
- 函数声明
- 函数声明会提到最前
- 如果变量对象已存在,则替换
- 变量声明
- 如果变量名称跟一家声明的形参或者函数相同,则不会干扰
var a = function(){}此类函数的字面量声明是变量声明,声明阶段
console.log(a()); // 1
var a = 2
function a() {
return 1
}
console.log(a); // 2
以上代码块,无论var a = 2 和 function a 如何交换位置,输出值不变
TDZ 暂时性死区:为了避免这些反人类的感官,ES6 提供了
let、const声明方式
let/const会令区块形成封闭的作用域,若在声明之前使用变量,就会报错
作用域链
多个执行上下文的变量对象构成的链表,能冲下往上的查找变量
this
执行上下文栈
在我们写代码中,不止有一个函数,也就是说会有多个执行上下文的创建和执行
为了统一管理执行上下文,js 引擎创建了一个执行上下文栈来统一管理各个上下文
- js 执行,压入全局执行上下文。这个全局上下文只有在应用程序结束或者页面关闭的时候,才会被弹出
- 当一个感受被调用啥时,创建执行上下文并压入。当函数执行结束,弹出
闭包
MDN 解释如下:
闭包是指那些能访问自由变量的函数
自由变量是指:
自由变量是指那些能在函数中使用,但既不是函数参数也不是函数局部变量的变量,及不属于函数执行上下文的变量
即,闭包等价于函数 + 函数能访问的自由变量
从技术角度说,闭包等价于函数
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = () => console.log(i);
}
data[0](); // 2
data[1](); // 2
data[2](); // 2
data[i] 函数访问的 i 是 for 循环内部的 i,因此永远返回 3
我们在创建一个执行上下文,将每个 i 保存下来
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = ((i) => () => console.log(i))(i)
}
data[0](); // 0
data[1](); // 1
data[2](); // 2
es6 中的 let 和 const 会形成块级作用域,因此也可以解决该问题