官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
一、javascript闭包的特点:
1.作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
2.一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
简单的说,javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
二、javascript闭包的原理
闭包是指有权访问另一个函数作用域中的变量的函数。根据下面的代码示例来理解什么是闭包,在add函数内部的匿名函数中,访问到外部函数的变量outerArg,在执行add(10)之后外部函数返回了,并且将内部的匿名函数赋值给了变量addTen,此时通过addTen调用函数,依然可以访问到outerArg,也就是10。这个闭包中的变量,只能通过调用addTen函数访问,无法通过其他渠道访问到,下面代码最后一行通过输出属性的方式尝试访问结果是输出undefined。
outerArg是属于add函数的作用域中的变量,addTen有权访问add函数作用域中的变量,因此addTen是一个闭包。闭包产生的本质是:在一个函数(外部函数)内部定义的函数(内部函数)会将外部函数作用域中的活动对象添加到自己的作用域链中,下面代码中inner函数将add函数的outerArg添加到自己的作用域链上。在add函数执行完之后,其执行环境会被销毁,但由于inner函数还在引用outerArg,所以outerArg不会被销毁,依然保留在inner函数的作用域链中。直到inner函数(addTen函数)被销毁之后,outerArg才会跟着其作用域链一起被销毁。由于闭包变量是位于作用域链上,因此必须调用闭包函数进入其作用域之后才能访问到闭包变量。
function add(outerArg) {
function inner(innerArg) {
return innerArg + outerArg;
}
return inner;
}
var addTen = add(10);
console.log(addTen(1)); // 输出11
console.log(addTen(2)); // 输出12
console.log(addTen(3)); // 输出13
console.log(addTen.outerArg); // undefined123456789101112
三、javascript闭包的应用
javascript闭包的应用1:
这个是我在用js模拟排序算法过程遇到的问题。我要输出每一次插入排序后的数组,如果在循环中写成
setTimeout(function() { $(“proc”).innerHTML += arr + “《br/》”; }, i * 500);
会发现每次输出的都是最终排好序的数组,因为arr数组不会为你保留每次排序的状态值。为了保存会不断发生变化的数组值,我们用外面包裹一层函数来实现闭包,用闭包存储这个动态数据。下面用了2种方式实现闭包,一种是用参数存储数组的值,一种是用临时变量存储,后者必须要深拷贝。所有要通过闭包存储非持久型变量,均可以用临时变量或参数两种方式实现。
《!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”》
《html xmlns=“http://www.w3.org/1999/xhtml”》
《head》
《title》《/title》
《script type=“text/javascript”》《!--
var arr = [4, 5, 6, 8, 7, 9, 3, 2, 1, 0];
var $ = function(id) { return document.getElementById(id); }
var Sort = {
Insert: function() {
for (var i = 1; i 《 arr.length; i++) {
for (var j = 0; j 《 i; j++) {
if (arr[i] 《 arr[j]) {
arr[i] = [arr[j], arr[j] = arr[i]][0];
}
}
setTimeout((function() {
var m = [];
for (var j = 0; j 《 arr.length; j++) {
m[j] = arr[j];
}
return function() {
$(“proc”).innerHTML += m + “《br /》”;
}
})(), i * 500);
//or 写成下面这样也可以
/*
setTimeout((function(m) {
return function() {
$(“proc”).innerHTML += m + “《br /》”;
}
})(arr.join(“,”)), i * 500);
*/
}
return arr;
}
}
// --》《/script》
《/head》
《body》
《div》
var a = [4, 5, 6, 8, 7, 9, 3, 2, 1, 0];《/div》
《div》
《input type=“button” value=“插入排序” onclick=“Sort.Insert();” /》
《/div》
Proc:《br /》
《div id=“proc”》
《/div》
《/body》
《/html》
javascript闭包的应用2:
这个是无忧上的例子,为每个《li》结点绑定click事件弹出循环的索引值。起初写成
id.onclick = function(){ alert(i); } id.onclick = function(){alert(i);}
发现最终弹出的都是4,而不是想要的 1、2、3,因为循环完毕后i值变成了4。为了保存i的值,同样我们用闭包实现:
《!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”》
《html xmlns=“http://www.w3.org/1999/xhtml”》
《head》
《title》《/title》
《script type=“text/javascript”》《!--
window.onload = function() {
for (var i = 1; i 《 4; i++) {
var id = document.getElementById(“a” + i);
id.onclick = (function(i) {
return function() {
alert(i);
}
})(i);
}
}
// --》《/script》
《/head》
《body》
《ul》
《li id=“a1”》aa《/li》
《li id=“a2”》aa《/li》
《li id=“a3”》aa《/li》
《/ul》
《/body》
《/html》
(ps:var a = (function(){})(); 与 var a =new function(){}效果是一样的,均表示自执行函数。)
javascript闭包的应用3:
下面的code是缓存的应用,catchNameArr。在匿名函数的调用对象中保存catch的值,返回的对象由于被CachedBox变量引用导致匿名函数的调用对象不会被回收,从而保持了catch的值。可以通过CachedBox.getCatch(“regionId”);来操作,若找不到regionId则从后台取,catchNameArr 主要是为了防止缓存过大。
《script type=“text/javascript”》
var CachedBox = (function() {
var cache = {}, catchNameArr = [], catchMax = 10000;
return {
getCatch: function(name) {
if (name in cache) {
return cache[name];
}
var value = GetDataFromBackend();
cache[name] = value;
catchNameArr.push(name);
this.clearOldCatch();
return value;
},
clearOldCatch: function() {
if (catchNameArr.length 》 catchMax) {
delete cache[catchNameArr.shift()];
}
}
};
})();
《/script》
同理,也可以用这种思想实现自增长的ID。
《script type=“text/javascript”》
var GetId = (function() {
var id = 0;
return function() {
return id++;
}
})();
var newId1 = GetId();
var newId2 = GetId();
《/script》
评论
查看更多