【转载】轻松理解JS闭包

Closures allow JavaScript programmers to write better code. Creative, expressive, and concise. We frequently use closures in JavaScript, and, no matter your JavaScript experience, you will undoubtedly encounter them time and again. Sure, closures might appear complex and beyond your scope, but after you read this article, closures will be much more easily understood and thus more appealing for your everyday JavaScript programming tasks.

This is a relatively short (and sweet) post on the details of closures in JavaScript. You should be familiar with JavaScript variable scope before you read further, because to understand closures you must understand JavaScript’s variable scope.

What is a closure?

A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain. The closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.

The inner function has access not only to the outer function’s variables, but also to the outer function’s parameters. Note that the inner function cannot call the outer function’s arguments object, however, even though it can call the outer function’s parameters directly.

You create a closure by adding a function inside another function.

A Basic Example of Closures in JavaScript:?

 1 function showName (firstName, lastName) {?
 2 ?  var nameIntro = "Your name is ";
 3   // this inner function has access to the outer function‘s variables, including the parameter?
 4 ?  function makeFullName () {?
 5   ?  return nameIntro + firstName + " " + lastName;?
 6   }
 7 ?
 8   ?return makeFullName ();?
 9 }?
10 ?
11 showName ("Michael", "Jackson"); // Your name is Michael Jackson?

Closures are used extensively in Node.js; they are workhorses in Node.js’ asynchronous, non-blocking architecture. Closures are also frequently used in jQuery and just about every piece of JavaScript code you read.

A Classic jQuery Example of Closures:?

1 $(function() {
2 ?
3   ?var selections = [];
4   $(".niners").click(function() { // this closure has access to the selections variable?
5     selections.push (this.prop("name")); // update the selections variable in the outer function‘s scope?
6   });
7 ?
8 });

Closures’ Rules and Side Effects

  • Closures have access to the outer function’s variable even after the outer function returns

  One of the most important and ticklish features with closures is that the inner function still has access to the outer function’s variables even after the outer function has returned. Yep, you read that correctly. When functions in JavaScript execute, they use the same scope chain that was in effect when they were created. This means that even after the outer function has returned, the inner function still has access to the outer function’s variables. Therefore, you can call the inner function later in your program. This example demonstrates:

  

 1 function celebrityName (firstName) {
 2     var nameIntro = "This celebrity is ";
 3     // this inner function has access to the outer function‘s variables, including the parameter?
 4    function lastName (theLastName) {
 5         return nameIntro + firstName + " " + theLastName;
 6     }
 7     return lastName;
 8 }
 9 ?
10 ?var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.?
11 ?
12 ?// The closure (lastName) is called here after the outer function has returned above?
13 ?// Yet, the closure still has access to the outer function‘s variables and parameter?
14 mjName ("Jackson"); // This celebrity is Michael Jackson?
  • Closures store references to the outer function’s variables

  they do not store the actual value. ?Closures get more interesting when the value of the outer function’s variable changes before the closure is called. And this powerful feature can be harnessed in creative ways, such as this private variables example first demonstrated by Douglas Crockford:

  

 1 function celebrityID () {
 2     var celebrityID = 999;
 3     // We are returning an object with some inner functions?
 4     // All the inner functions have access to the outer function‘s variables?
 5     return {
 6         getID: function ()  {
 7             // This inner function will return the UPDATED celebrityID variable?
 8             // It will return the current value of celebrityID, even after the changeTheID function changes it?
 9           return celebrityID;
10         },
11         setID: function (theNewID)  {
12             // This inner function will change the outer function‘s variable anytime?
13             celebrityID = theNewID;
14         }
15     }
16 }
17 var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.?
18 mjID.getID(); // 999?
19 mjID.setID(567); // Changes the outer function‘s variable?
20 mjID.getID(); // 567: It returns the updated celebrityId variable?
  • Closures Gone Awry

  Because closures have access to the updated values of the outer function’s variables, they can also lead to bugs when the outer function’s variable changes with a for loop. Thus:

  

 1 // This example is explained in detail below (just after this code box).?
 2 ?function celebrityIDCreator (theCelebrities) {
 3     var i;
 4     var uniqueID = 100;
 5     for (i = 0; i < theCelebrities.length; i++) {
 6       theCelebrities[i]["id"] = function ()  {
 7         return uniqueID + i;
 8       }
 9     }
10     return theCelebrities;
11 }
12 ?
13 ?var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
14 ?
15 ?var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
16 ?
17 ?var stalloneID = createIdForActionCelebs [0];??console.log(stalloneID.id()); // 103

In the preceding example, by the time the anonymous functions are called, the value of i is 3 (the length of the array and then it increments). The number 3 was added to the uniqueID to create 103 for ALL the celebritiesID. So every position in the returned array get id = 103, instead of the intended 100, 101, 102.

The reason this happened was because, as we have discussed in the previous example, the closure (the anonymous function in this example) has access to the outer function’s variables by reference, not by value. So just as the previous example showed that we can access the updated variable with the closure, this example similarly accessed the i variable when it was changed, since the outer function runs the entire for loop and returns the last value of i, which is 103.

To fix this side effect (bug) in closures, you can use an Immediately Invoked Function Expression (IIFE), such as the following:

 1 function celebrityIDCreator (theCelebrities) {
 2     var i;
 3     var uniqueID = 100;
 4     for (i = 0; i < theCelebrities.length; i++) {
 5         theCelebrities[i]["id"] = function (j)  { // the j parametric variable is the i passed in on invocation of this IIFE?
 6             return function () {
 7                 return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array?
 8             } () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.?
 9         } (i); // immediately invoke the function passing the i variable as a parameter?
10     }
11 ?
12     return theCelebrities;
13 }
14 ?
15 ?var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
16 ?
17 ?var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
18 ?
19 ?var stalloneID = createIdForActionCelebs [0];
20 ?console.log(stalloneID.id); // 100?
21 ?
22 ?var cruiseID = createIdForActionCelebs [1];?console.log(cruiseID.id); // 101
时间: 2024-11-05 20:34:57

【转载】轻松理解JS闭包的相关文章

javascript深入理解js闭包(看了挺多的,感觉这篇比较透彻)

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function

轻松理解js的函数和构造函数的区别

如何轻松理解js的函数和构造函数的区别,这是个一直头大的问题,很多例子都没有清晰的描述清楚.. 在这里,我就用平常的道理来阐述一下,希望能理解清楚. 从这里开始入手吧 这是普通函数的定义和调用方式.看起来没什么特别的,但是往下看就有奇怪的东西了. 再做一个: 你没发先一个很奇怪的现象吗? 你的这个函数里面并没有返回什么,也就是没有return ,但是你调用的时候却可以接受啊. 如:var p=new Person('niexiaoqian'); alert(p.name); //niexiaoq

理解js闭包原理

各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂.我的理解是,闭包就是能够读取其他函数内部变量的函数. 由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数". 所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁. 闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中. 怎么来理解这句话呢?请看下面的

轻松理解JavaScript闭包

摘要 闭包机制是JavaScript的重点和难点,本文希望能帮助大家轻松的学习闭包 一.什么是闭包? 闭包就是可以访问另一个函数作用域中变量的函数.下面列举出常见的闭包实现方式,以例子讲解闭包概念 function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000 f1是

【转载】javascript深入理解js闭包

一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码: var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码: function f1(){ var n=999; } alert(n); // error 这里有一个地方需要注意,

javascript深入理解js闭包

一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function f1(){ var n=999; } alert(n); // error 这里有一个地方需要注意,函数

深入理解js闭包

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function

javascript深入理解js闭包(摘自网络)

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function

(转)javascript深入理解js闭包

一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function f1(){ var n=999; } alert(n); // error 这里有一个地方需要注意,函数