飞道的博客

JavaScript基础之闭包

226人阅读  评论(0)

一、闭包(closure)

来自红宝书: 闭包是指有权访问另外一个函数作用域中的变量的函数。关键在于下面两点:

  • 是一个函数
  • 能访问另外一个函数作用域中的变量(即使另外一个函数上下文已经销毁)

来自MDN:函数对其周围状态(词法环境)的引用。

  • 自由变量是指在函数中使用的,但既不是函数参数arguments也不是函数的局部变量,其实就是另外一个函数作用域中的变量。

从作用域链理解闭包

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

例子

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}

var foo = checkscope(); // foo指向函数f
foo();					// 调用函数f()

简要的执行过程如下:

  1. 进入全局代码,创建全局执行上下文,全局执行上下文压入执行上下文栈
  2. 全局执行上下文初始化
  3. 执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 执行上下文被压入执行上下文栈
  4. checkscope 执行上下文初始化,创建变量对象、作用域链、this等
  5. checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出
  6. 执行 f 函数,创建 f 函数执行上下文,f 执行上下文被压入执行上下文栈
  7. f 执行上下文初始化,创建变量对象、作用域链、this等
  8. f 函数执行完毕,f 函数上下文从执行上下文栈中弹出

函数f 执行上下文维护了一个作用域链,会指向指向checkscope作用域,作用域链是一个数组,结构如下

fContext = {
    Scope: [AO, checkscopeContext.AO, globalContext.VO],
}

所以指向关系是当前作用域 --> checkscope作用域–> 全局作用域,即使 checkscopeContext 被销毁了,但是 JavaScript 依然会让 checkscopeContext.AO(活动对象) 活在内存中,f 函数依然可以通过 f 函数的作用域链找到它,这就是闭包实现的关键。

面试中的闭包

var data = [];

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

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

执行data[0]函数的时候,data[0]的作用域为全局作用域,由于自身没有i变量,就会向上查找,所以从全局上下文查找到i为3(此时for循环已经结束)。data[1]和data[2]是一样的。

解决方法

  • 使用闭包
// ...
for (var i = 0; i < 3; i++) {
  data[i] = (function (i) {
      return function(){
          console.log(i);
      }
  })(i);
}
// ...
  • 使用es6中的let
// ...
for (let i = 0; i < 3; i++) {
    data[i] = function() {
        console.log(i)
    }
}
// ..

转载:https://blog.csdn.net/jacktonyG/article/details/106082262
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场