var number = 2; var obj = { number : 4, fn1 : ( function() { this.number *= 2; number=number*2; var number=3; return function() { this.number *= 2; number*=3; alert(number); } } )(), db2:function(){this.number*=2} }; var fn1 = obj.fn1; alert(number);//问这会会弹出什么结果 4 fn1();//这会弹出什么结果 9 obj.fn1();//这次弹出什么结果 27 alert(window.number); //这会window.number的结果是什么 8 alert(obj.number); //这会obj.number的结果是什么 8
这是我无意间看到的一道js题,当时理解了好久,不过总算明白了,下面附上自己的解析。
一、编译阶段
var number = 2; var obj = { number : 4, fn1 : ( function() { this.number *= 2; number=number*2; var number=3; return function() { this.number *= 2; number*=3; alert(number); } } )(), db2:function(){this.number*=2} };
先看以上这段代码,其中包括了一个IIFE(Immediately-Invoked Function Expression )立即执行函数表达式(function(){})()。这个函数在编译阶段就执行了。主要作用是隔离作用域,模仿块级作用域。
为了方便理解,我在代码中添加alert();
var number = 2; var obj = { number : 4, fn1 : ( function() { this.number *= 2; alert(this); //window number=number*2; alert(number); //NAN 原因:变量声明提前 var number=3; alert("执行"); return function() { this.number *= 2; number*=3; //alert(number); } } )(), db2:function(){this.number*=2} };
可以看到IIFE的function的this指向的是window,并且在函数编译阶段就执行了,所以运行以上代码就会依次打印:
window
NAN
执行
并且 全局number=4;
obj.number=4;
IFFE 中存在的var number=3;(由于闭包的存在使IIFE的活动对象仍会存在)
二、执行阶段
var fn1 = obj.fn1; alert(number);//问这会会弹出什么结果 4 fn1();//这会弹出什么结果 9 obj.fn1();//这次弹出什么结果 27 alert(window.number); //这会window.number的结果是什么 8 alert(obj.number); //这会obj.number的结果是什么 8
1、var fn1 = obj.fn1;使fn1指向了IFFE的闭包,闭包的存在
2、alert(number);//此时是指全局number=4;
3、fn1();//调用fn1()函数,闭包中this的原则是是哪个对象调用该函数,this就指向哪个对象。此时是window对象调用。因此,
this.number 是指全局number(既是变量也当作window属性),全局 number=8;
变量的number根据作用域链以及闭包的规则,优先在内部环境找(无),再往上一级找(匿名函数中 var number=3 有) 所以var number=9;所以再alert(number)就为9。
4、obj.fn1();//obj调用fn1,obj.number=8,变量的number根据作用域链以及闭包的规则,优先在内部环境找(无),再往上一级找(匿名函数中 var number=9 有) 所以var number=27;所以再alert(number)就为27。
5.此时window.number=8;
6.此时obj.number=8;
重点:
理解IIFE的隔绝作用域,理解闭包的作用域链。