一:作用域
说起变量第一个要说到的肯定就是作用域,正是因为不熟悉JS的 作用域,往往就会把面向对象的作用域张冠李戴,毕竟有些东西总是习惯性的这样,但是并不是每次照搬都是可以的,那么下一个问题就来了,js到底是什么作用域,当然是函数作用域了,我们的浏览器就是一个被实例化的window对象,如果在window下定义一个name字段,那么name字段就具有window这个函数作用域,也就是在window下都是可以访问的,如果在window下定义一个function print,然后里面再定义一个name,那么这个新定义的name只能在print函数下通用,而老的name继续在window下通用,举个例子。
var name = "name1"; function print(){ var name = "name2"; console.log(name); } print(); console.log(name);
从图中可以看出两点:
1: 在window下定义了一个name,居然还可以在function下定义一个重名的name,这个在C#里面是不可想象的。
2:在JS下,它只认自己的作用域,所以就出现了第一个"liu",你可能觉得这个没有什么稀奇的地方,这是因为可能你还没有真正理解什么是函数作用域,解析器在执行print的时候,第一件事情就是寻找print下的所有局部变量,然后再执行后续语句,既然是先寻找,那么var name="print"这条语句定义在ctrip中任何位置都是可以的,下面我们把语句调换过来。
var name = "name1"; function print(){ console.log(name); var name = "name2"; } print(); console.log(name);
可以看到在print函数下,第一个console.log输出的是undefined,这个结果可以证实,确实做了第一件事情是收集到了name这个局部变量,可能有人说为什么没有变成" liu ",那是因为初始化操作必须是逐语句执行,所以在ctrip函数中执行console.log(name)时,此时解析器只知道有一个未赋值的变量name,所以就console的时候就是undefined了。
二:作用域链
从上面的例子中我们也很清楚的知道了,在function中定义的变量只具有function范围内的作用域,同时我们也看到上面这个例子只是一层嵌套,window是个大的function,里面是一个print的function,同样的道理也可以延伸到多层嵌套,比如三层,四层。。。。N层,这些层就形成了一个链式结构。
var name = "name1"; function print(){ var name = "name2"; console.log(name); function newprint(){ var name = "name3"; console.log(name); } newprint(); } print(); console.log(name);
从图中可以看到,我在print下再定义了一个newprint函数,这样的话就有三层了,输出的结果也是我们希望看到的,每层的name只在自己的作用域范围内生效,但是下面有一个问题来了,有时,在定义newprint的函数时,把 var name="name3" 中的var忘记写了,那么这个时候,newprint中的name到底是什么值呢? 是first还是second呢?
var name = "name1"; function print(){ var name = "name2"; console.log(name); function newprint(){ name = "name3"; console.log(name); } newprint(); } print(); console.log(name);
仔细想想会发现,当代码执行到newprint函数中的name="name3"时,发现newprint函数中并没有name这个局部变量,恰好代码又在print这个大函数中,所以解析器就会回溯到print函数中寻找name,发现果然有name,这个时候就把print的name修改成了" name3"。
另外一种情况,在定义newprint函数的时候,不小心把name="name3" 错写成了 nam="name3"; 丢了一个e,又不是我代码的问题。那么这个时候解析器该怎么处理?同样的道理,在回溯时,发现print没有,再回溯到顶层的window下,发现还是没有,这个时候解析器做了这样的处理,既然整个链中都没有,你又赋值了,我总不能给你报错,那多尴尬呀,就索性给你在window下隐式的定义一个nam变量,这个时候nam其实就是全局变量了。我们可以在window顶层console一下nam看看。
var name = "name1"; function print(){ var name = "name2"; console.log(name); function newprint(){ nam = "name3"; console.log(name); } newprint(); } print(); console.log(name); console.log(nam);