一、作用域
- 所有未定义的变量直接赋值会自动声明为全局作用域的变量(隐式全局变量可以用delete删除,var定义的则不行)
- window对象的所有属性拥有全局作用域
- 内层作用域可以访问外层作用域,反之不行
- var声明的变量,在除了函数作用域之外,在其他块语句中不会创建独立作用域
- let和const声明的变量存在块语句作用域,且不会变量提升
- 同作用域下不能重复使用let、const声明同名变量,var可以,后者覆盖前者
- for循环的条件语句的作用域与其循环体的作用域不同,条件语句块属于循环体的父级作用域
// 以下语句使用let声明不报错,说明为不同作用域
for (let i = 0; i < 5; i++) {
let i = 5
}
// 此语句报错,说明循环体为条件语句块的子作用域
// for循环执行顺序为:条件语句块1->条件语句块2->循环体->条件语句块3->条件语句块2 依次类推
for (let i = 0; i < 5; i=x) { // x is not defined
let x = 5
}
二、作用域链
- 作用域链也就是所谓的变量查找的范围
- 在当前作用域引用变量时,如果没有此变量,则会一路往父级作用域查找此变量,直到全局作用域,如果都没有,在非严格情况下会自动声明,所以是undefined,在严格条件下则会报错
- 变量的查找路径依据的是在创建这个作用域的地方向上查找,并非是在执行时的作用域,如下 b变量的值为2。可以看出当执行到需要b变量时,当前作用域下并没有b,所以要到定义这个b变量的静态作用域中寻找,即创建时候的作用域链上查找b的值
b = 1
function a() {
// 定义b,找到
const b = 2
function s() {
// 使用到b,当前作用域并没有,向上找
console.log(b);
}
return s
}
const s = a()
var b = 3
s() // 2
三、执行上下文
预编译参考 https://juejin.im/post/6844903940249616397
四、闭包
- 所谓闭包就是函数与其词法环境(创建当前作用时的任何局部变量)的引用。闭包可以使内部函数访问到外部函数的作用域,当函数被创建时即生成闭包。
- 当你从函数内部返回一个内部函数时,返回的函数将会保留当前闭包,即当前词法环境。
- 闭包只会保留环境中任何变量的最后一个值,这是因为闭包所保存的是整个变量的对象。
闭包的作用域链包含着它自己的作用域,以及包含它父级函数的作用域和全局作用域。
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
暴露闭包的方式不止返回内部函数一种,还可以使用回调函数产生闭包环境,或者把内部函数赋值给其他外部对象使用。
- 闭包在没有被外部使用的情况下,随执行结束销毁,如何产生闭包并且保留闭包环境的关键就在于不让其环境被垃圾回收系统自动清除,那么就要使内部环境中的引用被外部保留,这样才能保留闭包。
- 闭包虽然方便我们操作和保留内部环境,但是闭包在处理速度和内存消耗方面对脚本性能具有负面影响,除非在特定的情况下使用。