JavaScript中的闭包(Closure)

在上一篇介绍JavaScript this 关键字的文章中我们提到了闭包这个概念。闭包是指有权访问另一个函数作用域中的变量的函数。从函数对象中能够对外部变量进行访问(引用、更新),是构成闭包的条件之一。创建闭包的常见方式,就是在一个函数内部创建另一个函数。为了理解闭包,先来看一下什么是变量的生命周期。

变量的声明周期,就是变量的寿命,相对于表示程序中变量可见范围的作用域来说,生命周期这个概念指的是一个变量可以在多长的周期范围内存在并能够被访问。看下面一个例子:

functionextent() {
  var n = 0;
  return function() {
    n++;
    console.log("n=" + n);
    };
}
var returnFun = extent();
returnFun(); //"n=1"
returnFun(); //"n=2"

局部变量 n 是在 extent 函数中声明的,这个从属于外部作用域中的局部变量,被函数对象给封闭在里面了。被封闭起来的变量的寿命,与封闭它的函数对象的生命周期相同,即闭包延长了局部变量的声明周期。

闭包的概念比较抽象,来来回回反反复复就是那一句话,并不好理解,下面主要通过几个例子来看一下如何使用闭包。

1. 第一个例子:在循环中使用闭包

<!-- JavaScriptSnippet_1 Begin -->
function createFunction() {
  var result = [];
  for(var i = 0; i < 10; i++) {
    result[i] = function() {
      return i;
    };
  }
  return result;
};
//调用
result[0](); //9
result[1](); //9
… …
result[9](); // 9
<!-- JavaScriptSnippet_1 End -->
 
<!-- JavaScriptSnippet_2 Begin -->
function createFunction() {
  var result = [];
  for(var i = 0; i < 10; i++) {
    (function(num) {
      result[i] = function() {
        return num;
      };
})(i);
  }
  return result;
}
//调用
result[0](); //0
result[1](); //1
… …
result[9](); //9
<!-- JavaScriptSnippet_2 End -->
 
<!-- JavaScriptSnippet_3 Begin -->
function createFunction() {
  var result = [];
  for(var i = 0; i < 10; i++) {
    result[i] = (function(num) {
      return function() {
        return num;
      };
    })(i);
  }
  return result;
}
//调用
result[0](); //0
result[1](); //1
… …
result[9](); //9
<!-- JavaScriptSnippet_3 End -->
 
<!-- JavaScriptSnippet_4 Begin -->
function createFunction() {
  var result = [];
  for(var i = 0; i < 10; i++) {
    result[i] = (function(num) {
      return function() {
        return num;
      };
    }(i));
  }
  return result;
}
//调用
result[0](); //0
result[1](); //1
… …
result[9](); //9
<!-- JavaScriptSnippet_4 End -->
 
<!-- JavaScriptSnippet_5 Begin -->
function createFunction() {
  var result = [];
  for(var i = 0; i < 10; i++) {
    result[i] = function(num) {
      return function() {
        return num;
      };
    }(i);
  }
  return result;
}
//调用
result[0](); //0
result[1](); //1
… …
result[9](); //9
<!-- JavaScriptSnippet_5 End -->

2. 第二个例子:结合例一再次理解一下闭包

<!-- HTML Begin-->
<pid="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name:  <input type="text" id="name" name="name"></p>
<p>Age:  <input type="text" id="age" name="age"></p>
<!-- HTML End-->
<!--JavaScript Common Snippet Begin -->
function showHelp(info) {
  document.getElementById(‘help’).innerHTML =info;
}
//为了之后调用
function closureFunc(info) {
  return function() {
    showHelp(info);
  };
}
function setupHelp() {
  var helpText = [
    {id: "email", info: "Please Input YourEmail Address"},
    {id: "name", info: "Please Input Your Name"},
    {id: "age", info: "Please Input Your Age"}
  ];
  <!-- JavaScript Common Snippet End -->
 
<!-- JavaScript Snippet_1 Begin --><!-- Can Not -->
for(var i = 0; i < helpText.length; i++) {
  var item = helpText[i];
  document.getElementById(item.id).onfocus = function() {
    showHelp(item.info);
  }
}
<!-- JavaScript Snippet_1 End -->
 
<!-- JavaScript Snippet_2 Begin --><!-- Can -->
for(var i = 0; i < helpText.length; i++) {
  var item = helpText[i];
   document.getElementById(item.id).onfocus = closureFunc(item.info);
}
<!-- JavaScript Snippet_2 End -->
 
<!-- JavaScript Snippet_3 Begin --><!-- Can -->
for(var i = 0; i < helpText.length; i++) {
  (function(num) {
    var item = helpText[num];
     document.getElementById(item.id).onfocus = function() {
      showHelp(item.info);
    };
  })(i);
}
<!-- JavaScript Snippet_3 End -->
 
<!-- JavaScript Snippet_4 Begin --><!-- Can -->
for(var i = 0; i < helpText.length; i++) {
  var item = helpText[i];
   document.getElementById(item.id).onfocus = (function(info) {
    return function() {
      showHelp(info);
    };
  })(item.info);
}
<!-- JavaScript Snippet_4 End -->
 
<!-- JavaScript Snippet_5 Begin --><!-- Can -->
for(var i = 0; i < helpText.lenght; i++) {
  var item = helpText[i];
   document.getElementById(item.id).onfocus = ( function(info) {
    return function() {
      showHelp(info);
    };
  }(item.info) );
}
<!-- JavaScript Snippet_5 End -->
 
<!-- JavaScript Snippet_6 Begin --><!-- Can -->
for(var i = 0; i < helpText.length; i++) {
  var item = helpText[i];
   document.getElementById(item.id).onfocus = function(info) {
    return function() {
      showHelp(info);
    };
  }(item.info);
}
<!-- JavaScript Snippet_6 End -->
}
setupHelp();

代码片段6也能成功,但建议最好使用括号将其括起来,即推荐使用代码片段4或代码片段5的形式。如果直接在JavaScript中这样写:

function(info) {
  return function() {
    showHelp(info);
  };
}(item.info);

会导致错误,这是因为JavaScript 把 function 关键字当作一个函数声明的开始,而函数声明后面不能跟圆括号,然而函数表达式后面可以跟圆括号。要想将函数声明转换为函数表达式,只要像下面这样给它加上一对圆括号:

(function(info){
  return function() {
    showHelp(info);
  };
})(item.info);

3. 第三个例子:创建公有方法访问私有变量

var Counter = (function() {
  var privateCounter = 0;
  var changeBy = function(val) {
    privateCounter += val;
  };
  return {
    getCounter: function() {
      return privateCounter;
    },
    setCounter: function(newCounter) {
      privateCounter = newCounter;
    },
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    }
  };
})();
//调用
Counter.privateCounter //undefined
Counter.getCounter(); //0
Counter.setCounter(100);
Counter.getCounter(); //100
Counter.increment();
Counter.getCounter(); //101
Counter.decrement();
Counter.getCounter(); //100
 
//改造为工厂类
var makeCounter = function() {
  var privateCounter = 0;
  var changeBy = function(val) {
    privateCounter += val;
  };
  return {
    getCounter: function() {
      return privateCounter;
    },
    setCounter: function(newCounter) {
      privateCounter = newCounter;
    },
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    }
  };
};
//生成及调用
var Counter_1 = makeCounter(),
   Counter_2 = makeCounter();
Counter_1.getCounter(); //0
Counter_2.getCounter(); //0
Counter_1.setCounter(88);
Counter_2.getCounter(); //0

4. 第四个例子:模仿块级作用域

在 Java ,C 等许多编程语言中都有块级作用域的概念,以 Java 为例:

for(int i = 0; i< 10; i++) {
  //do something
}
System.out.println(i); //i cannot be resolved to a variable

而 JavaScript中没有块级作用域的概念,如:

for(var i = 0; i< 10; i++) {
  //do something
}
var i;
console.log(i); //10
var i = 5;
console.log(i); //5

很多情况下为了不污染全局命名空间,可以使用闭包来模拟块级作用域:

(function() {
  var just_a_number = 100;
  console.log(just_a_number); //100
})();
console.log(just_a_number);//Uncaught ReferenceError: just_a_number is not defined

关于闭包就说这么多,重在理解。

完。

参考/扩展资料:

《JavaScript高级程序设计(第3版)》 作者: Nicholas C.Zakas  译者:李松峰曹力

http://www.zhihu.com/question/19554716

http://www.cnblogs.com/dolphinX/archive/2012/09/29/2708763.html

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

http://bonsaiden.github.io/JavaScript-Garden/zh/#function.closures

时间: 2024-10-10 03:56:11

JavaScript中的闭包(Closure)的相关文章

javascript中的闭包(Closure)的学习

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 下面是我在网上通过学习阮一峰老师的笔记,感觉总结很不错,特记录于此. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. var n = 999; function f1() { alert(n); } f1(); //999 另一方面,在函

javascript中的闭包解析

学习javaScript已经有一段时间了,在这段时间里,已经感受到了JavaScript的种种魅力,这是一门神奇的语言,同时也是一门正在逐步完善的语言,相信在大家的逐步修改中,这门语言会逐步的完善下去,在上一篇随笔中,和大家分享了JavaScript中独有的类中的继承方式,今天呢,就跟大家分享一下我这几天一直在搞,却还是搞的不是很透彻的闭包问题,对于一个初学者而言,JavaScript中的闭包无疑是一个难点,而且也是我们必须要掌握的一个重点,那么今天我就跟大家分享一下我在学习闭包的时候的感悟以及

javascript中的闭包。

function todo() { var var1 = 1; (function () { var var2 = var1 + 1; alert(var2); })(); } todo(); (function(){})()是javascript里的闭包.可以在这个里面调用外面的js变量.但是外面的js变量不能调用里面的变量. javascript中的闭包.,布布扣,bubuko.com

javascript中的闭包、模仿块级作用域和私有变量

闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见方式为:在一个函数内部创建另一个函数. "当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链.然后,使用arguments和其他命名参数的值来初始化函数的活动对象(activation object).但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象出于第三位.....直至作用域链终点的全局执行环境." function creawteCompariso

Javascript中的闭包(转载)

前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它.下面是作者从作用域链慢慢讲到闭包以及在后面提到了一些闭包的高级用法.下面大家一起来学习Javascript中的闭包. 谈一谈JavaScript作用域链 当执行一段JavaScript代码(全局代码或函数)时,JavaScript引擎会创建为其创建一个作用域又称为执行上下文(Execution Context),在页面加载后会

一篇文章把你带入到JavaScript中的闭包与高级函数

在JavaScript中,函数是一等公民.JavaScript是一门面向对象的编程语言,但是同时也有很多函数式编程的特性,如Lambda表达式,闭包,高阶函数等,函数式编程时一种编程范式. function dada() { var a = 1; var b = function() { console.log(a); } return b // b 就是一个闭包函数,因为它能访问dada函数的作用域 } JavaScript的函数也是对象,可以有属性,可以赋值给一个变量,可以放在数组里作为元素

Javascript中的闭包 O__O &quot;…

一.闭包!? 闭包(closure)是Javascript语言的一个难点,对于初学者来说不容易理解,那我们先来看看闭包的含义. 百度百科与"官方"解释:所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 维基百科:在编程语言中,闭包(也称为词法闭包或函数闭包)是用于在具有第一类函数的语言中实现词法范围名称绑定的技术. 然而我们看到这些官方的解释并不能很清楚的了解闭包到底是什么鬼东西,只会一头问号,

浅谈javascript中的闭包

引入定义:闭包只有权访问另一个函数中的作用域中的函数. 简单点说,就是当某函数a执行完毕后,闭包不会使得GC(JavaScript的回收机制)去回收a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量. 代码示例: window.onload = function(){ function createComparisonFunction(propertyName){ return function(object1, object2){ var value1 = object1[proper

浅析 JavaScript 中的闭包(-------------------------------------------)

一.前言 对于 JavaScript 来说,闭包是一个非常强大的特征.但对于刚开始接触的初学者来说它又似乎是特别高深的.今天我们一起来揭开闭包的神秘面纱.闭包这一块也有很多的文章介绍过了,今天我就浅谈一下自己对闭包的的一些理解,希望能提供一点鄙陋的见解帮助到正在学习的朋友.该文章中能使用口语化的我将尽量使用口语化的叙述方式,希望能让读者更好理解,毕竟文章写出来宗旨就是要让人读懂.文章难免有不足之处还希望帮忙指出. 二.Javascript 的作用域链 在了解闭包之前,我们先来看看几个准备知识.