根据一段代码浅谈Javascript闭包

(编辑:jimmy 日期: 2024/10/12 浏览:2)

复制代码 代码如下:
function f1(){
var n = 999;
nAdd = function(){ n += 1; }
function f2(){
alert(n);
}
return f2;
}

这里的闭包是f1,封闭了一个变量n和一个函数f2。

我们先无视nAdd,尽量保持原貌重写一下这个函数。
复制代码 代码如下:
function f1(){
var n = 999;
var f2 = function(){ alert(n); };
return f2;
}
var result = f1();
result();

js中各个变量以function为单元进行封装,当在function内部找不到某一变量时,function会向其所在的上一单元(上下文)中进行查找,一直查找到顶层的window域。
这时就出现一个疑问:这个查找过程是以函数引用位置为起点还是函数体定义的位置为起点?
在上面这一段代码中,result所在域是window,但是实际的输出结果是f1内部的n值,所以可以得出结论:变量查找的起点是函数体定义的位置。

现在再回过头来看nAdd(第一段代码)。如我们所知,没有关键字var定义的变量默认进入window域,所以nAdd实际为window.nAdd。这就等同于如下代码:
复制代码 代码如下:
var nAdd;
function f1(){
var n = 999;
nAdd = function(){ n += 1; }
function f2(){
alert(n);
}
return function(){ alert(n); };
}

那么根据我们对result的分析,nAdd的执行将影响f1中n的值。
所以有:
复制代码 代码如下:
function f1(){
var n = 999;
nAdd = function(){ n += 1; }
function f2(){
alert(n);
}
return function(){ alert(n); };
}
var result = f1();
result();
nAdd();
result();

这段代码执行最终的输出结果为1000。

再看这种情况:
复制代码 代码如下:
function f1(){
var n = 999;
nAdd = function(){ n += 1; }
function f2(){
alert(n);
}
return function(){ alert(n); };
}

f1()(); //<--p1
nAdd();
f1()(); //<--p2

简述一下执行过程:
p1位置,f1封装了一个匿名的闭包A,在返回A闭包中的函数A:f2后继而执行A:f2,A:f2输出变量A:n,结果是999。
与此同时,nAdd被赋值为A闭包中的一个函数,下一行执行nAdd即让A:n的值+1。
p2位置,f1封装匿名的闭包B,所进行的操作都是针对闭包B的,随后执行B:f2输出的是B:n,所以最后的结果依然是999。
A和B是两个独立的“包”,互不影响。

改写一下函数的调用部分:

复制代码 代码如下:
function f1(){
var n = 999;
nAdd = function(){ n += 1; }
function f2(){
alert(n);
}
return function(){ alert(n); };
}

var result = f1();
result();
nAdd();
f1()();
result(); // <--p3

p3位置不意外地输出了1000。

一句话新闻

微软与英特尔等合作伙伴联合定义“AI PC”:键盘需配有Copilot物理按键
几个月来,英特尔、微软、AMD和其它厂商都在共同推动“AI PC”的想法,朝着更多的AI功能迈进。在近日,英特尔在台北举行的开发者活动中,也宣布了关于AI PC加速计划、新的PC开发者计划和独立硬件供应商计划。
在此次发布会上,英特尔还发布了全新的全新的酷睿Ultra Meteor Lake NUC开发套件,以及联合微软等合作伙伴联合定义“AI PC”的定义标准。