码迷,mamicode.com
首页 > 其他好文 > 详细

理解函数作用域与闭包

时间:2016-01-11 11:52:21      阅读:135      评论:0      收藏:0      [点我收藏+]

标签:

前言

但凡读书,或者学一门技术,都要问自己以下几个问题。

  • 它是什么?
  • 它有什么用?/发明它是为了解决什么问题?
  • 它有什么弊端?

我下面就试着从这几个方向来阐述闭包这个概念。

概念

在了解闭包之前,我们需要了解几个概念。本文在这里只做简单介绍,如需要进一步了解,请参考文章末尾的链接。

作用域

变量和函数的可作用范围,分为局部作用域和全局作用域。Javascript不具有块级作用域,而具有函数作用域。

执行环境(execuation context)

变量和函数有权访问的其他数据。

执行环境栈(execuation context stack)

每个函数在执行的时候,会把它的执行环境推入一个栈中,在函数执行完毕后执行环境出栈并被销毁。保存在其中的所有函数和比变量定义随之销毁,控制权返回到之前的执行环境中。全局的执行环境在应用程序退出(浏览器关闭)才会被销毁。

作用域链(scope chain)

作用域链用于保证对执行环境有权访问的变量和函数的有序访问。

什么是闭包?

闭包这个概念,在函数式编程里很常见,简单的说,就是使内部函数可以访问定义在外部函数中的变量。严格一点的定义是

在函数内声明另一个函数,并且返回这个函数。这个返回的函数和它的执行环境整体叫做闭包。

让我们来看一个例子:

function f1(){
  var val = 10;
}
console.log(val);    //Uncaught ReferenceError: val is not defined(…)

由于从函数外部无法访问函数内部的变量,所以报出了错误。那么如何能够访问到局部作用域的变量呢?

function f1(){
  var val = 10;
  function f2(){
    console.log(val);
  }
  return closure;
}

var f2 = f1();
f2();           // 10

在这段代码中,f2 函数和其执行环境构成了一整个闭包。对于常规的 f1() 方法, 在其内部的变量 val 应该在 f1() 方法执行完毕以后就被垃圾回收。但是 f1() 返回了一个新的方法 f2()。由于 f2() 访问了其外部函数的变量 val,val就构成了f2函数的执行环境。val 存在于f2的作用域链中,只要f2()方法没有被销毁,其作用域链中的变量和函数就不会被销毁, val 也就会一直存在。

闭包有什么用?

for循环变量无法保持的问题

for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 5);
}

上面这个代码块会打印五个 5 出来,而我们预想的结果是打印 1 2 3 4 5。

之所以会这样,是因为 setTimeout 中的 i 是对外层 i 的引用。当 setTimeout 的代码被解释的时候,运行时只是记录了 i 的引用,而不是值。而当 setTimeout 被触发时,五个 setTimeout 中的 i 同时被取值,由于它们都指向了外层的同一个 i,而那个 i 的值在迭代完成时为 5,所以打印了五次 5

为了得到我们预想的结果,我们可以把 i 赋值成一个局部的变量,从而摆脱外层迭代的影响。

for (var i = 0; i < 5; i++) {
  (function (idx) {
    setTimeout(function () {
      console.log(idx);
    }, 5);
  })(i);
}

制造函数构造器

假如我们要实现一系列的函数:add10,add20。我们为此构造了一个名为 adder 的构造器,如下:

var adder = function (x) {
  var base = x;
  return function (n) {
    return n + base;
  };
};

var add10 = adder(10);
console.log(add10(5));

var add20 = adder(20);
console.log(add20(5));

每次调用 adder 时,adder 都会返回一个函数给我们。我们传给 adder 的值,会保存在一个名为 base 的变量中。由于返回的函数在其中引用了 base 的值,于是 base 的引用计数被 +1。当返回函数不被垃圾回收时,则 base 也会一直存在。

闭包有什么弊端?

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

 

参考:
学习Javascript闭包(Closure)

node-lessons/lesson11 at master · alsotang/node-lessons · GitHub

JavaScript作用域链

理解函数作用域与闭包

标签:

原文地址:http://www.cnblogs.com/timl525/p/5106221.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!