一个题目引发的闭包、函数声明以及作用域的简单思考
在segmentfault的微博看到了这样的一个标题,传送门:javascript关于闭包的面试题,顺手点开发现了一段这样的代码
1 function f1(){ 2 var n=999; 3 nAdd=function(){n+=1} 4 function f2(){ 5 alert(n); 6 } 7 return f2; 8 } 9 var result=f1(); 10 result(); // 999 11 nAdd(); 12 result(); // 1000
这里倒是没什么问题,result为function f2(){alert(n);},nAdd为function(){n+=1;}。
在f1的声明时决定了,result和nAdd二者对f1作用域内的n是共享的,因此也得出了相应的结果。
但评论中的例子引出了一些困惑:
1 function f1(){ 2 var n = 999; 3 nAdd = function(){ n += 1;} 4 function f2(){ 5 alert(n); 6 } 7 return f2; 8 } 9 var result1 = f1(); 10 var result2 = f1(); 11 12 result1();//999 13 result2();//999 14 nAdd(); 15 result1();//999 16 result2();//1000
按之前所述,n不是共享的么?怎么这里只有result2里的n变成了1000?发生了什么?
这么看来一定是f1里的n发生了问题,转个弯从头开始查起
一、当f1函数定义时发生了什么
寄出大杀器 由ecma的官方声明大致了解到,当f1定义时,它并没有理会函数内部是什么,将{}中的内容装进[[code]]这个大口袋,等待调用。换句话说这时候它只是做了一个安静的f1,大括号内部是什么全不理会。
二、那f1调用了,[[code]]见光了,又发生了什么
确实当到了line9和line10这里,
var result1 = f1();
var result2 = f1();
f1被调用时,n nAdd f2终于有人来认养了,三个娃儿出来哽咽的说:“终于可以吃饱饭不用被关小黑屋了”...
那result1和result2的n为啥不一个样儿?
领养三个娃儿的流浪汉--作用域轻咳一声:“本是我(result1)领养的n nAdd两个娃,我的二弟(result2)看着欢喜,就复制了俩娃,可惜二娃nAdd太胖复制不动,我(result1)就送我弟(result2)了...”
言归正传,result1和result2定义在全局,且都被赋值f1(),此时实际就有了对应result1和result2的两个作用域,以及相应两个作用域内的n,而nAdd在每次调用f1()时都会重新定义,直接调用nAdd会在新的作用域内执行。这也就解释了为什么
nAdd();
之后为什么只有result2里的n变化了。
下面做个简单的验证:
function f1(){ var n = 999; nAdd = function(){ n += 1;} function f2(){ alert(n); } return f2; } nAdd(); //报错,nAdd undefined,证实f1定义没理会{}内部 var result1 = f1(); nAdd(); //对f1内的n操作 var result2 = f1(); result1();//1000 result2();//999 //证实了result1和result2内的n无关 nAdd(); result1();//1000 result2();//1000
如有错误,欢迎提出,验证后一定更改。