作用域和作用域链
转自 http://blog.sina.com.cn/s/blog_68cb8c790100ysmj.html
有了昨天关于变量的概念,今天学起作用域就会稍微轻松一些。 作用域是javascript最关键的部分之一,想在javascript上更上一层,这块知识必不可少,必须弄懂。 作用域: 下面就通俗的说说什么是作用域:作用域就是变量与函数的可访问范围。在javascript中分为全局作用域和局部作用域。
- 全局作用域:在代码中任何地方都能访问到的对象拥有全局作用域。在Web浏览器中,他指的就是window对象,该环境直到应用程序退出才被摧毁。
- 局部作用局:和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到(最常见的例如函数内部,所以在一些地方也会看到有人把这种作用域称为函数作用域),该环境中的代码被执行完毕后,该环境被摧毁。
(一)全局作用域有3种情况: 1.最外层函数和在最外层函数外面定义的变量拥有全局作用域(举一个我想了很久都没明白的例子,不过现在明白了)
alert(nameOne); //1
alert(nameTwo); //出现错误 因为nameTwo是在doSomething()里定义的局部变量,这里不能使用
doSomething(); //2
innerThing(); //出现错误 因为innerThing()是在doSomething()里定义的局部函数,这里不能使用 看上面这一段很简单的代码,nameOne就是最外层函数外面定义的变量,doSomething()是最外层函数,他们就是上面标题中所说的,他们拥有全局作用域,在任何地方都可以访问到他们!! 再看后面我给出了解释,也可以很明确的就知道为什么会出现错误了。 2.所有末定义直接赋值的变量自动声明为拥有全局作用域 看我举的一个简单的例子: function doSomething (){
var testOne = "1";
testTwo = "2";
}
doSomething(); alert(testOne);//输出错误 如图:
alert(testTwo);//输出"2" 这段代码先声明doSomething函数,里面包含了两个变量:testOne和testTwo,然后调用doSomething函数,输出testOne和testTwo,alert(testOne)之所以会出现错误是因为var操作符使他成为了局部变量。alert(testTwo)而这里输出了正确的结果"2",是因为:所有末定义直接赋值的变量自动声明为拥有全局作用域。
3.所有window对象的属性拥有全局作用域 一般情况下,window对象的内置属性都都拥有全局作用域,例如window.name、window.location、window.top等等。 (二)局部作用域 有了上面的概念和例子应该已经可以很好的理解局部作用域了,如果还想更清楚的了解下,我把上面的例子拆分出来,如下: function doSomething (){
var testOne = "1";
function anotherThing(){
alert(testOne);
}
anotherThing();
}
doSomething();//输出"1",因为doSomething是全局函数
alert(testOne);//错误,因为testOne是被定义到doSomething函数中的局部变量,外部不可被访问
anotherThing();//错误,anotherThing函数是doSomething函数中的局部函数,外部不可被访问 以上就是全部作用域的概念和例子,希望大家能明白,如果我写的有错误可以及时给予指正,谢谢!下面来说说作用域链
作用域链: 有了作用域的基础,学作用域链就会游刃有余。 函数对象有一个内部属性是[[Scope]],该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链(Scope chain)。 当一个函数创建后,它的作用域链会被创建此函数的作用域中可访问的数据对象所填充。例如: function add (num1,num2){
var sum = num1 + num2;
return sum;
} 当函数add()创建后,它的作用域链中填入了一个单独的对象,这个对象表示所有范围定义的变量。该全局对象包括诸如window,navigator和document等等。如图说明了他们的关系: 函数add的作用域会在被执行时用到: var total = add (5,10); 执行这个函数时会创建一个称为"运行期上下文(execution context)"的内部对象。这个对象相当重要,下面会反复提到他。一个运行期上下文定义了一个函数值执行时的环境,他是独一无二的,当函数运行完毕,它就会被摧毁。 如果对运行期上下文这个对象还是不明白看下面这个图:
下面说下图中运行的过程:(对应图中的1,2,3) 1.当运行期被创建时,它的作用域链初始化为当前运行函数的[[Scope]]属性中所包含的对象。 2.这些值按照他们出现在函数中的顺序,被复制到作用域链中。 3.上面2过程一旦完成,一个被称为"活动对象"的新对象就会被创建,他被推入到作用域链的前端。 4. 当1(运行期上下文)被摧毁,3(活动对象)也随之被摧毁。
为了让自己更加明白作用域链,我又从网上找了几个例子,和大家一起分享,学习: 第一个例子: var num = 1;
function total(num){
alert(num);
var num = 2;
}
total(10); 这个例子很简单,可能不用了解作用域链都可以很清楚的知道它的结果是10,因为函数total有个形参num,调用他的时候传给他一个实参10,在输出的时候结果就是10。 那么再来看第二个例子:(先别看答案,猜猜结果) var num = 1;
function total(){
alert(num);
var num = 2;
}
var num = 10;
total(); 我就做错了,我想这题肯定是迷惑我让我以为是10,我想的答案是2,我是这么想的:现在没有参数了,直接调用total函数,它会在它当前作用域中搜索num这个变量的值,结果自然为2,可是我忽略了var num = 2;是在alert(num);之后才声明的,所以最终结果是undefined。 这个例子等价于: var num = 1;
function total(){ var num;//默认为undefined
alert(num);
num = 2;
}
var num = 10;
total(); 改变作用域链:可以通过with语句和try-catch语句中得catch子句,但并不建议使用他们,因为改变了作用域链,代码的效率会大大降低,我会在另外一遍笔记中写关于高性能javascript(数据存储)的效率问题。

浙公网安备 33010602011771号