关于闭包

转自 http://blog.youkuaiyun.com/chenmoquan/

啥是闭包?


一个闭包就是一个可以访问外部函数作用域链的一个内部函数。闭包有三个作用域链:它可以访问自己的作用域(即当前的大括号内),它也可以访问外部函数的变量,它还可以访问全局变量。

闭包不仅可以访问外部函数的变量,还可以访问外部函数的参数。注意:尽管内部函数可以直接访问外部函数的参数,但并不能调用外部函数的arguments 对象。

我们可以通过在一个function里增加一个function来创建一个闭包。

一个简单的闭包示例 in JavaScript:


function showName (firstName, lastName) {

​var nameIntro = "Your name is ";
// this inner function has access to the outer function's variables, including the parameter​
​function makeFullName () {

​ return nameIntro + firstName + " " + lastName;

}

​return makeFullName ();

}


showName ("Michael", "Jackson"); // Your name is Michael Jackson

闭包在node.js中广泛使用,它是node.js的异步和无阻塞的工具。在jQuery以及几乎每一段js代码中,闭包都被广泛使用。

jQuery中一段经典的闭包代码:

$(function() {

​var selections = [];
$(".niners").click(function() { // this closure has access to the selections variable​
selections.push (this.prop("name")); // update the selections variable in the outer function's scope​
});

});

闭包的特性及副作用

1. 外部函数返回后,闭包依然可以访问外部函数中的变量

这是闭包中重要的特性之一。是的,你没看错。在js函数执行过程中,内部函数与外部函数使用相同的作用域链。这意味着即使外部函数返回之后,内部函数依然可以访问其中的变量。所以我们可以稍后在程序中调用内部函数。
示例代码:

function celebrityName (firstName) {
var nameIntro = "This celebrity is ";
// this inner function has access to the outer function's variables, including the parameter​
function lastName (theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return lastName;
}

​var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.​

​// The closure (lastName) is called here after the outer function has returned above​
​// Yet, the closure still has access to the outer function's variables and parameter​
mjName ("Jackson"); // This celebrity is Michael Jackson


2. 闭包保存了对外部函数中的变量的引用

并不是保存实际的变量值。 当闭包被调用之前,如果外部函数的变量发生变化时,闭包会很有意思。闭包的这种特性有很多用途。例如下面这个私有变量的例子(first demonstrated by Douglas Crockford):


function celebrityID () {
var celebrityID = 999;
// We are returning an object with some inner functions​
// All the inner functions have access to the outer function's variables​
return {
getID: function () {
// This inner function will return the UPDATED celebrityID variable​
// It will return the current value of celebrityID, even after the changeTheID function changes it​
return celebrityID;
},
setID: function (theNewID) {
// This inner function will change the outer function's variable anytime​
celebrityID = theNewID;
}
}

}

​var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.​
mjID.getID(); // 999​
mjID.setID(567); // Changes the outer function's variable​
mjID.getID(); // 567: It returns the updated celebrityId variable


3. 闭包很乱

因为闭包可以访问外部函数的变量,所以在for循环中当外部函数的变量发生变化时容易出bug。

// This example is explained in detail below (just after this code box).​
​function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function () {
return uniqueID + i;
}
}

return theCelebrities;
}

​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];

​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);

​var stalloneID = createIdForActionCelebs [0];

console.log(stalloneID.id()); // 103

在前面的例子中,匿名函数被调用时i的值是3。所以每个返回的array 都会得到 i=103,而不是 100, 101, 102。原因是闭包访问外部函数的变量,是通过引用,并不是直接访问值。前面的例子展示了我们可以通过闭包访问更新的变量,这个例子也是在i变化时访问它,因为外部函数运行整个循环返回的最后的i值是103。

为了修复这个bug,可以使用一个立即执行函数表达式 Immediately Invoked Function Expression (IIFE), 代码如下:

function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function (j) { // the j parametric variable is the i passed in on invocation of this IIFE​
return function () {
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​
} () // 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.​
} (i); // immediately invoke the function passing the i variable as a parameter​
}

return theCelebrities;
}

​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];

​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);

​var stalloneID = createIdForActionCelebs[0];

console.log(stalloneID.id); // 100​

​var cruiseID = createIdForActionCelebs[1];

console.log(cruiseID.id); // 101
<think>好的,用户需要关于前端笔试中闭包的编程题。首先,我需要回忆闭包的定义和在前端面试中的常见应用场景。闭包是指函数能够访问并记住其词法作用域外的变量,即使函数在外部作用域之外执行。这在JavaScript中常用于封装私有变量、模块化开发等。 接下来,我需要从常见的面试题中筛选涉及闭包的编程题。可能的问题包括计数器、延迟打印、私有变量实现等。例如,循环中使用闭包解决var声明变量的问题,或者实现一个只能通过特定方法访问的变量。 然后,要确保每个问题都明确考察闭包的理解和应用,并提供清晰的示例代码。同时,需要注意代码的正确性和可读性,避免语法错误。例如,在循环中创建闭包来保存每个迭代的变量状态,或者使用闭包实现模块模式。 另外,用户可能需要详细的解答,所以每个问题后面需要给出解决方案和解释,说明闭包在其中的作用。例如,解释为什么在循环中使用闭包可以解决变量共享的问题,或者如何利用闭包创建私有变量。 还要检查引用内容,虽然用户提供的引用中没有直接提到闭包,但需要确保回答符合前端笔试的常见题型,可以参考类似的问题结构。比如引用[2]中的链表反转题,可以类比闭包问题如何考察作用域和函数的概念。 最后,生成相关问题时要覆盖闭包的不同应用场景,确保用户能全面理解闭包的各种用法和潜在问题。例如,闭包的内存泄漏问题、闭包在事件处理中的应用等。</think>### 前端笔试闭包编程题解析 闭包是JavaScript中函数与其词法作用域结合的机制,常用于保存状态或实现数据封装。以下是3道典型闭包编程题及解答: --- #### 题目1:实现计数器生成器 要求:创建函数`createCounter()`,每次调用返回的计数器函数时,数值+1,初始值为0 示例: ```javascript const counter = createCounter(); console.log(counter()); // 0 console.log(counter()); // 1 ``` **解答**: ```javascript function createCounter() { let count = -1; // 通过闭包保存计数状态 return function() { count++; return count; } } ``` 闭包特性:内部函数捕获外部函数的`count`变量,形成独立作用域链[^2] --- #### 题目2:循环中延迟打印 要求:修正以下代码使其输出0-4 ```javascript for(var i=0; i<5; i++){ setTimeout(() => console.log(i), 100) } ``` **解答**: ```javascript for(var i=0; i<5; i++){ (function(j){ // IIFE创建闭包作用域 setTimeout(() => console.log(j), 100) })(i) } ``` 闭包作用:每个迭代创建独立作用域,捕获当前`i`值[^3] --- #### 题目3:实现私有变量 要求:创建对象包含`getValue`和`add`方法,外部无法直接访问数值 ```javascript const obj = createPrivateVar(10); obj.add(5); console.log(obj.getValue()); // 15 ``` **解答**: ```javascript function createPrivateVar(init) { let value = init; // 闭包保存私有变量 return { getValue: () => value, add: n => value += n } } ``` 闭包应用:通过函数作用域实现数据封装,形成模块模式[^1] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值