这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
这篇文章主要介绍了 Javascript 变量的作用域和作用域链详解, 本文用一个实例和运行结果来讲解这两个知识, 需要的朋友可以参考下
工作这几年,js 学的不是很好,正好周末有些闲时间,索性买本《js 权威指南》,大名鼎鼎的犀牛书,好好的把 js 深入的看一看。买过这本书的第一印象就是贼厚,不过后面有一半部分都是参考手册。
一:作用域
说起变量第一个要说到的肯定就是作用域,正是因为不熟悉 JS 的作用域,往往就会把面向对象的作用域张冠李戴,毕竟有些东西总是习惯性的这样,但是并不是每次照搬都是可以的,那么下一个问题就来了,js 到底是什么作用域,当然是函数作用域了,我们的浏览器就是一个被实例化的 window 对象,如果在 window 下定义一个 name 字段,那么 name 字段就具有 window 这个函数作用域,也就是在 window 下都是可以访问的,如果在 window 下定义一个 function ctrip,然后里面再定义一个 name,那么这个新定义的 name 只能在 ctrip 函数下通用,而老的 name 继续在 window 下通用,举个例子。
从图中可以看出两点:
1: 在 window 下定义了一个 name,居然还可以在 function 下定义一个重名的 name,这个在 C# 里面是不可想象的。
2:在 JS 下就可以做到眼瞎,它只认自己的作用域,所以就出现了第一个 "second",你可能觉得这个没有什么稀奇的地方,这是因为可能你还没有真正理解什么是函数作用域,解析器在执行 ctrip 的时候,第一件事情就是寻找 ctrip 下的所有局部变量,然后再执行后续语句,既然是先寻找,那么 var name="second" 这条语句定义在 ctrip 中任何位置都是可以的,下面我们把语句调换过来。
可以看到在 ctrip 函数下,第一个 console.log 输出的是 undefined,这个结果可以证实,确实做了第一件事情是收集到了 name 这个局部变量,可能有人说为什么没有变成 "second",那是因为初始化操作必须是逐语句执行,所以在 ctrip 函数中执行 console.log(name) 时,此时解析器只知道有一个未赋值的变量 name,所以就 console 的时候就是 undefined 了。
二:作用域链
从上面的这个例子中我们也很清楚的知道了,在 function 中定义的变量只具有 function 范围内的作用域,同时我们也看到上面这个例子只是一层嵌套,window 是个大的 function,里面是一个 ctrip 的 function,同样的道理也可以延伸到多层嵌套,比如三层,四层。。。。N 层,这些层就形成了一个链式结构。
从图中可以看到,我在 ctrip 下再定义了一个 plane 函数,这样的话就有三层了,输出的结果也是我们希望看到的,每层的 name 只在自己的作用域范围
内生效,但是下面有一个问题来了,有一天我傻逼了,在定义 plane 的函数时,把 var name="third" 中的 var 忘记写了,那么这个时候,plane 中的
name 到底是什么值呢? 是 first 还是 second 呢?
- var name="first";
- function ctrip(){
- var name="second";
- function plane(){
- name="third";
- console.log(name);
- }
- plane();
- console.log(name);
- }
- ctrip();
- console.log(name);
现在就是考验你是否真的懂了作用域链,仔细想想会发现,当代码执行到 plane 函数中的 name="third" 时,发现 plane 函数中并没有 name 这个局部变量,恰好代码又在 ctrip 这个大函数中,所以解析器就会回溯到 ctrip 函数中寻找 name,发现果然有 name,这个时候就把 ctrip 的 name 修改成了 "third"。
又有一天,我喝多了酒又傻逼了一回,在定义 plane 函数的时候,把 name="third" 错写成了 nam="third"; 丢了一个 e,你可以说是酒精的问题,
又不是我代码的问题。那么这个时候解析器该怎么处理呢?同样的道理,在回溯时,发现 ctrip 没有,再回溯到顶层的 window 下,发现还是没有,
这个时候解析器做了这样的处理,既然整个链中都没有,你又赋值了,我总不能给你报错,那多尴尬呀,就索性给你在 window 下隐式的定义一个
nam 变量,这个时候 nam 其实就是全局变量了。我们可以在 window 顶层 console 一下 nam 看看。
好了,关于变量的东西也就这么多了,没什么稀奇的,理解了就没什么意思了。
来源: http://www.phperz.com/article/17/0415/272003.html