闭包变量作用域导致的undefined问题
作者:秋了秋 发表时间:2016年08月14日
在JavaScript中“熟练”掌握并理解变量的作用范围非常必要,搞混变量作用域将会带来各式各样的奇葩问题,而且是难以查找的问题,尤其在复杂的代码环境中。对变量的作用范围心如明镜也能提高开发效率。什么时候我应该大肆书写变量名而不用考虑变量重复,什么时候应该谨慎处理,明白这些才能做到写代码犹如写文章,洋洋洒洒!
爆出一个我最近遇到的一个问题,就是关于局部变量后定义导致的undefined的问题,估计很少人会遇到这种情况,无意粗心就碰到了这个问题,特此我写了个简单的例子来阐述下(把它叫做“问题函数”吧):
function douwo(a,b){
console.log("第一层:a:"+a+",b:"+b);
var arg=arguments.length;
$("html").click(function(){
console.log("第二层:a:"+a+",b:"+b);
if(arg>2){
var b=8;
}
});
}
douwo(1,2);//第一层:a:1,b:2
我把闭包函数的b=8误写成了var b=8;众所周知,函数的参数相当于该函数的局部变量。上面的代码可以写成:
function douwo(){
var a=1,b=2;
console.log("第一层:a:"+a+",b:"+b);
var arg=arguments.length;
$("html").click(function(){
console.log("第二层:a:"+a+",b:"+b);
if(arg>2){
var b=8;
}
});
}
douwo();//第一层:a:1,b:2
这是很好理解的,关键不好理解的是当点击页面的时候,打印出来的是:第二层:a:1,b:undefined 可是一个函数中变量后定义有时候也会存在,科普下(针对新手),如:
function qlq(){
console.log(a);//undefined
var a=8;
}
变量分声明和定义,当一个变量用var关键字的时候是声明,=赋值是定义,而在函数内变量后声明定义会把声明提前,而定义保留在原有的位置。所以上面的代码可以写成:
function qlq(){ var a;//声明,但未赋值 console.log(a);//undefined a=8;//定义,赋值 }
而函数参数都会充当局部变量在内部提前声明,所以最上面的代码可以写成:
function douwo(a,b){
var a,b;
console.log("第一层:a:"+a+",b:"+b);
var arg=arguments.length;
$("html").click(function(){
console.log("第二层:a:"+a+",b:"+b);
if(arg>2){
var b=8;
}
});
}
douwo(1,2);//第一层:a:1,b:2
外面那层很好理解,关键在于里面那个闭包函数:
$("html").click(function(){
console.log("第二层:a:"+a+",b:"+b);//第二层:a:1,b:undefined
if(arg>2){
var b=8;
}
});
这又要来科普下计算机对变量的查找规则了:就近原则,所谓就近原则就是首先是从函数内部查找变量,如果没有找到就往外层找,注意,外层可能是父函数,或者全局(window),如果全局都找不到那就真的不存在这个变量了。
关键难以理解的是if(arg>2){var b=8;},理论上条件不成立就不会进到这if里面去,当然就不会执行var b=8;所以上一行的打印会出来外函数的a和b,但是事实不是这样的,虽然条件不成立不会执行,但是计算机还是阅读了这段代码,就像小学老师批改周记签上“已阅”,计算机阅过之后的代码是这样子的:
function douwo(a,b){ var a,b; console.log("第一层:a:"+a+",b:"+b);//第一层:a:1,b:2 var arg=arguments.length; $("html").click(function(){ var b; console.log("第二层:a:"+a+",b:"+b);//第二层:a:1,b:undefined if(arg>2){ b=8; } }); } douwo(1,2);
声明了但条件不成立所以没有定义,最终问题函数输出:第二层:a:1,b:undefined也就理解通了~, 这是个坑,巨大的坑!