第13期 作用域和闭包

136 阅读2分钟

作用域的分类

全局作用域

全局对象(window)下的属性和方法

最外层声明的变量和方法

非严格模式下 无声明定义的变量

函数作用域

函数作用域内生成的变量和方法

块级作用域

通过函数自执行来模拟的块级作用域 原理还是函数作用域

作用域链

是由当前执行环境与上层执行环境的一系列变量对象组成的,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。

当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链。

作用域链的顶端是全局对象

执行上下文栈与作用域链

执行上下文栈是在代码执行时确定,作用域是在创建执行上下文时确定的。

作用域链和原型继承查找时的区别

如果去查找一个普通对象的属性,但是在当前对象和其原型中都找不到时,会返回undefined。但查找的属性在作用域链中不存在的话就会抛出ReferenceError。

闭包

闭包何时产生

闭包是一种特殊的对象。

它由两部分组成:执行上下文(代号A),以及在该执行上下文中创建的函数(代号B)。

当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。

许多书籍文章里都以函数B的名字代指这里生成的闭包 而在Chrome中,则以执行上下文A的函数名代指闭包

闭包的例子1

var data = [];

for (var i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}

data[0]();
data[1]();
data[2]();

3
3
3

用闭包的方式更改

var data = [];

for (var i = 0; i < 3; i++) {
  data[i] = (function (i) {
    return function(){console.log(i);}
  })();
}

data[0]();
data[1]();
data[2]();

闭包的例子2

// 用var定义i
var data = [];

for (let i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}

data[0]();
data[1]();
data[2]();

3
3
3
var data = [];
// 用let定义i
for (let i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}

data[0]();
data[1]();
data[2]();

1
2
3

使用let使得for循环为块级作用域

等同于

var data = [];
for (var i = 0; i < 3; i++) {
    (function(i){
        data[i] = function () {
            console.log(i);
        };
    })(i)
}