深入理解javascript作用域链

之前作用域链在我眼里也只是在调用一个对象时一层一层向上找到自己所需的变量或是函数,若没有则返回undefined,其实大致上说却是这样的,但是我需要的是不断的深入。

在深入理解之前先记住两句话

1.js中一切皆对象,函数也是对象

2.函数运行在他们被定义的作用域内,而不是被执行的作用域内。

当定义函数时,会包含[[scope]]属性(因为js中一切皆对象,函数也是对象),此属性是函数内部属性,只允许js引擎访问,[[scope]]指向作用域链(scope lain),而此时仅包含

所有全局变量。

当调用函数时,会创建"运行期上下文"内部对象,此对象包含自己的作用域链,用于解析标识符,并初始化为调用函数的内部对象[[scope]](即全局变量),这些值按照出现的位置

依次复制到运行期上下文中,一起组成"活动对象",并添加至作用域链的首位。活动对象包括此次调用函数内部的所有局部变量,形参,命名参数以及this。当运行期函数销毁时,

活动对象也随之销毁。

说了那么多,肯定已经懵了,来个例子消化一下。

function add(num1,num2) {
    var sum = num1 + num2;
    return sum;
}

当定义add函数时,创建[[scope]]属性,指向作用域链(scope chain),仅包含全局对象(global object)如下图:

在调用add函数时,创建“执行上下文”(execution context)并创建活动对象(activation object),更新作用域链,如下图:

在调用函数每遇到一个变量时,都会经历标识符解析从而知道从哪里获取数据并存储数据,查找是否有同名标识符,若没有则查找下一个对象,直到全局变量,若一直没有找到,则证明此标识符并没有被定义。

那么,来个小问题,为什么编写js代码时尽量避免使用全局变量?

答案:因为使用全局变量时,在作用域链中需查找到最后一层,影响性能。

根据上面的所讲的以及文章开始时的第二个注意问题,再举个例子

function output() {
    var name = "nana";
    var intro = function() {
        alert(name);
    };
    return intro;
}

function a(para) {
    var name = para;
    var func = output();
    func();
}
a(‘lili‘);

在调用函数a时作用域链为:

[[scope chain]] = {

pare: ‘lili‘,

name: undefined,

func: undefined,

arguments: []

},{

window call object

}

在调用函数output时作用域链为:(注意:并不包含a的活动对象)

[[scope chain]] = [{

name: undefined,

intro: undefined

},{

window call object

}]

在定义intro函数时作用域链为

[[scope chain]] = [{

name: ‘nana‘,

intro: undefined

},{

window call object

}]

当从output返回,在a函数中调用intro时,发生标志符解析,此时作用域链为:

[[scope chain]] = [{

intro call object

},{

name: ‘nana‘,

intro: ‘undefined‘

},{

window call object

}]根据文章开始的第二句话,函数运行在被定义的作用域内,而不是被执行的作用域内。如上intro函数运行在被定义的作用域内。因此,最后答案是nana。

改变作用域链

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").onclick=function(){
            doSomething();
        };
    }
}

with可以改变作用域,看似很方便,实际上却影响性能。当代码读到with时,作用域链又被改成下图所示,所以所有函数内部的局部变量都需要查找第二次才可被访问,影响了性能,避免使用。

2>try-catch

当try代码块发生异常时,则执行catch语句,此时作用域也会像上面那样临时被改动,那么,局部变量也是只能在第二次查找时才可匹配到,影响性能,但在调试时try-catch有很大帮助,所以不必完全避免。注意:在执行完catch语句时,作用域链又恢复回来。

优化:

1>尽量避免使用全局变量
2>缓存变量,将使用一次以上的全局变量缓存给一个局部变量。

预编译

每次说到作用域链的时候不得不提一下预编译

在每次预编译时,先找var关键字,再找function定义式,

看个例子吧!

console.log(a);
var a = 10;
console.log(a);

console.log(typeof func);
console.log(typeof d);
function func() {}
var d = function(){};
console.log(typeof d);

猜猜会输出什么呢?

答案是:undefined   10   function   undefined   function

你猜对了么?

首先来说变量,在预编译时找到var关键字并简单赋值为undefined,只有在执行时,才被赋值。var a = 10,相当于var a; a = 10; 因此,第一个输出undefined,第二个a已经执行了,所以输出10。

再来说函数,在预编译时找到函数定义式,而函数表达式只会在执行过程中才被执行,因此,第三个打印出function,第四个打印undefined,第五个打印function。

参考资料:
http://naotu.baidu.com/viewshare.html?shareId=av8cg0e3dckw&qq-pf-to=pcqq.group
http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html

时间: 2024-10-12 14:47:15

深入理解javascript作用域链的相关文章

JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript. JavaScript作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. 1.  全局作用域(Global S

(转)JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript. JavaScript作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. 全局作用域(Global Scope

JavaScript 开发进阶:理解 JavaScript 作用域和作用域链(转载 学习中。。。)

作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript. JavaScript作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. 1.  全局作用域(Global S

理解 JavaScript 作用域和作用域链

作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript. JavaScript作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. 1.  全局作用域(Global S

[转] JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript. JavaScript作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. 1.  全局作用域(Global S

JavaScript作用域链

JavaScript作用域链 之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时候发现作用域和执行环境确实很重要,又很基础,对理解JavaScript闭包很有帮助,所以在写一篇对作用域和执行环境的理解. 作用域 作用域就是变量和函数的可访问范围,控制着变量和函数的可见性与生命周期,在JavaScript中变量的作用域有全局作用域和局部作用域. 单纯

(好文推荐)一篇文章看懂JavaScript作用域链

闭包和作用域链是JavaScript中比较重要的概念,首先,看看几段简单的代码. 代码1: 1 var name = "stephenchan"; 2 var age = 23; 3 function myFunc() { 4 alert(name); 5 var name = "endlesscode"; 6 alert(name); 7 alert(age); 8 alert(weight); 9 } 10 myFunc(); 11 myFunc(); 上述代码

深入理解javascript作用域系列第二篇——词法作用域和动态作用域

× 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作模型导致的,作用域分为词法作用域和动态作用域,分清这两种作用域模型就能够对变量查找过程有清晰的认识.本文是深入理解javascript作用域系列第二篇——词法作用域和动态作用域 词法作用域 第一篇介绍过,编译器的第一个工作阶段叫作分词,就是把由字符组成的字符串分解成

【转】javascript 作用域链

JavaScript函数的作用域链分为定义时作用域链和运行时作用域链: 函数被定义的时候,它有一个属性[[scope]]标明它的定义作用域链,定义时作用域链[[scope]]遵守这样的规则:一个函数的定义时作用域链[[scope]]总是它所在的外部函数的执行时作用域链: 全局函数的定义作用域链只包含window的属性: 一个函数的执行时作用域链总是在定义时作用域链的头部压入当前活动对象(它包含this,arguments,参数,局部变量): 函数执行时,变量寻址总是从作用域链的顶端朝下寻找:所以