作用域及作用域链小结

作用域与作用域链知识小结

作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理。今天我在这里将作用域和作用域链做一个小小的总结,希望能帮到大家。

一.作用域的作用:

任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。

1.  全局变量

在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:

(1)最外层函数和在最外层函数外面定义的变量其执行环境是window,即全局环境,例如:

var color = "blue";  //定义一个全局变量

 function getcolor(){

 return color;

 }

 getcolor();

 alert(color);  //blue

这里color是一个全局变量,在函数getcolor中的color仍然是blue,全局变量也能作用在局部函数中。

(2)所有末定义直接赋值的变量自动声明为拥有全局作用域,例如:

color = "blue";  

 function getcolor(){

var color= “red”;

 return color;

 }

 getcolor();

 alert(color);  //blue

这里并未用var定义color,所以color会被自动解析成为一个全局变量,以至最后输出为blue。

3)所有没有块级的作用域,其当前执行环境可以为window,即全局环境,例如:

if(true){

 var color = "red";

 }

 alert(color);  //red;

if中定义的color也在其执行环境外生效,因为它不是块级作用域,在if中定义的变量会被添加到当前的执行环境中,它不像在c,c++,或java中,当执行完if后,将变量直接销毁,所有在for循环中要尤其注意这一点,如:

 for(var i=0;i<20;i++){

 var sum =0;

 sum+=i;

 }

 alert(sum);   //19;

 alert(i);   //20;

在运行过程中i被添加在当前执行环境,遂成为全局变量,所以for循环执行完后,i的值为20.

一般情况下,window对象的内置属性都拥有全局作用域,例如window.name、window.location、window.top等等。

2. 局部变量

与全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,在一些地方也会看到有人把这种作用域称为函数作用域。如:

var color = "blue";

function getcolor(){

var color = "red";  

return color;

}

alert(getcolor());   //red;

alert(color);      //blue;

这里在函数getcolor内声明的变量是一个局部变量,所以它的执行环境只是函数getcolor,当函数执行完毕后该变量便会被销毁,所以最后的alert输出的是blue,即全局变量的值,而不是重新给color赋值的red,只有在执行getcolor,即局部变量的作用环境的时候,该变量才会生效。再如:

function getcolor(){

var color = "red";

return color;

}

alert(getcolor());  //red;

alert(color);   //undefined;该color在函数外部是访问不到的。

此外,在局部作用域中定义的变量可以在局部环境中与全局变量互换使用,如下:

var color = "blue";

function change(){

var color1 = "red";

function getcolor(){

var color2 = color1;

color1 = color;

color = color2;  //这里可以访问color,color1,color2

}

getcolor();   //这里能访问color,color1

}

change();   //这里只能访问color

其作用环境关系如下图:

 

所以这里的color2只能作用在函数getcolor中,而color1可以作用在函数getcolor和change中,color则可以作用在全局,所以它可在函数getcolor中与color1,color2交换使用。

3.延长作用域链

函数每次执行时对应的运行期上下文都是独一无二的,所以多次调用同一个函数就会导致创建多个运行期上下文,当函数执行完毕,执行上下文会被销毁。每一个运行期上下文都和一个作用域链关联。一般情况下,在运行期上下文运行的过程中,其作用域链只会被 with 语句和 catch 语句影响。

1)with语句是对象的快捷应用方式,用来避免书写重复代码,如下:

function initUI(){

    with(document){

        var bd=body,

         links=getElementsByTagName("a"),

         i=0,

         len=links.length;

        while(i < len){

           update(links[i++]);

        }

        getElementById("btnInit").οnclick=function(){

            doSomething();

        };

    }

}

这里使用width语句来避免多次书写document,看上去更高效,实际上产生了性能问题。

  当代码运行到with语句时,运行期上下文的作用域链临时被改变了。一个新的可变对象被创建,它包含了参数指定的对象的所有属性。这个对象将被推入作用域链的头部,这意味着函数的所有局部变量现在处于第二个作用域链对象中,因此访问代价更高了,如下图:


因此在程序中应避免使用with语句,在这个例子中,只要简单的把document存储在一个局部变量中就可以提升性能。

2)try-catch语句中的catch语句。

try代码块中发生错误时,执行过程会跳转到catch语句,然后把异常对象推入一个可变对象并置于作用域的头部。在catch代码块内部,函数的所有局部变量将会被放在第二个作用域链对象中。如下:

try{

    doSomething();

}catch(ex){

    alert(ex.message); //作用域链在此处改变

}

请注意,一旦catch语句执行完毕,作用域链机会返回到之前的状态。try-catch语句在代码调试和异常处理中非常有用,因此不建议完全避免。你可以通过优化代码来减少catch语句对性能的影响。一个很好的模式是将错误委托给一个函数处理,例如:

try{

    doSomething();

}catch(ex){

    handleError(ex); //委托给处理器方法

}

优化后的代码,handleError方法是catch子句中唯一执行的代码。该函数接收异常对象作为参数,这样你可以更加灵活和统一的处理错误。由于只执行一条语句,且没有局部变量的访问,作用域链的临时改变就不会影响代码性能了

注意:

1.访问变量有按值和按引用两种,而参数只能按值进行传递,如下:

function add(num){

num+=10;

return num;

}

var count =20;

var result =add(count);

alert(count);   //20,没有变化

alert(result);   //30

这里的函数add有一个参数,而参数实际上是函数的局部变量,变量count作为参数别传入函数中,将其值赋给num,参数num被加上10,但是这个变化是不会影响函数外count的值的,因为参数num与变量count是互不相识的,只是有着相同的值而已,如果是按引用传递的,那么函数外部的count的值也是会被影响从而变成30.

再如:

function setName(obj) {

obj.name = "liwenqi";

obj = new Object();

obj.name = "lion";

}

var person = new Object();

setName(person);

alert(person.name);   //liwenqi

在函数外定义一个对象person,将这个对象传递给函数setName后,其name属性被设置为“liwenqi”,然后又将一个新的对象赋值给obj,此时再将name属性修改为lion。如果person是按引用传递的话,那么在修改name属性为“lion”后,person对象便会被自动修改为指向name属性值为“lion”的新对象,但是在输出perso.name时,其输出仍旧为“liwenqi”,这说明在函数内部修改参数的值,其原始引用仍旧未变。实际上,在函数内重新定义的obj对象,是一个局部对象,它在函数执行完毕后会被销毁。

2.声明变量他的作用域符合就近原则,即:会被添加到最近的执行环境中。

之前就说过,如果局部变量未加var声明,那么他会被加到全局环境中。如下:



3.作用域的最前端是当前的执行环境

挡在某个环境中为了读取或写入而引用一个标识符时,必须通过搜索来确定该标识符实际代表什么,搜索过程中从作用域链的最前端开始。如下:

var color="blue";

function getcolor(){

return color;

}

alert(getcolor()); //blue

其执行环境关系:return color---->getcolor()--->window;

var color="blue";

function getcolor(){

var color = "red";

return color;

}

alert(getcolor());  //red;

     alert(color);//blue

其执行环境关系:函数内部定义的color:return color---->getcolor(),而函数外部定义的color:color--->window。

4.标记的清除

这里可以通过翻转某个位来记录一个变量何时进入的执行环境,或者使用一个“进入执行环境”的变量列表及一个“离开环境”变量列表,来跟踪哪个变量发生了变化。

大部分的浏览器的js实现都使用标记清除式的垃圾收集策略。

调用:

window.collectGarbage()  //会立即执行垃圾的收集---->这是对于IE而言的。

window.Opera.collect()   //也会启动相同的效果,当然他是对于其他浏览器而言

5.管理内存

优化内存的最佳方式,就是为执行代码只保存必要的数据。

如:解除引用,一旦数据不再被使用,便将其值设置为null,用于将引用释放。

~~但是,他只适用于大多数全局变量和全局对象的属性。

~~其实解除引用的真正目的是让其脱离当前执行环境,方便下次垃圾回收的时候将其回收。

function person(name) {

var person = new Object();

person.name = name;

return person;

}

var localperson  = person("liwenqi");

localperson = null;  //手动解除localperson的引用。

 

 

小生的总结到此为止,希望对你有所帮助!



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值