JavaScript作用域学习笔记

作者: 7091a52ac9e5 | 来源:发表于2016-03-13 12:36 被阅读263次

    @(JS技巧)[JavaScript, 作用域]

    JavaScript作用域学习笔记

    概念:

    作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。

    分类:

    全局作用域(Global Scope)

    定义:
    在代码中任何地方都能访问到的对象拥有全局作用域

    出现的情况:

    1. 最外层函数和在最外层函数外面定义的变量拥有全局作用域;
    2. 所有未定义直接赋值的变量会自动声明为全局作用域;
    3. 所有window对象的属性;
    局部作用域

    定义
    只在固定的代码片段内可访问到,最常见的例如函数内部,所有在一些地方也会看到有人把这种作用域称为函数作用域;

    作用域链

    定义:
    JavaScript有一个内部属性称为Scope,由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。

    在JS中,作用域的概念和其他语言差不多, 在每次调用一个函数的时候 ,就会进入一个函数内的作用域,当从函数返回以后,就返回调用前的作用域.

    js作用域的执行过程
    1. 任何执行上下文时刻的作用域, 都是由作用域链(scope chain)来实现。
    2. 在一个函数被定义的时候, 会将它定义时刻scope chain链接到这个函数对象的[[scope]]属性.
    3. 在一个函数对象被调用的时候,会创建一个活动对象(也就是一个对象), 然后对于每一个函数的形参,都命名为该活动对象的命名属性, 然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中.
    作用域链
    函数执行的赋值过程

    在函数执行过程中,每遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是从活动对象开始搜索,查找同名的标识符,如果找到了就使用这个标识符对应的变量,如果没找到继续搜索作用域链中的下一个对象,如果搜索完所有对象都未找到,则认为该标识符未定义。函数执行过程中,每个标识符都要经历这样的搜索过程。

    注意: 因为函数对象的[[scope]]属性是在定义一个函数的时候决定的, 而非调用的时候,

    JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里

    Javascript的预编译
    1. JS是一种脚本语言, JS的执行过程, 是一种翻译执行的过程.
    2. 在JS中, 是有预编译的过程的, JS在执行每一段JS代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式).
    3. 在调用函数执行之前, 会首先创建一个活动对象, 然后搜寻这个函数中的局部变量定义,和函数定义, 将变量名和函数名都做为这个活动对象的同名属性, 对于局部变量定义,变量的值会在真正执行的时候才计算, 此时只是简单的赋为undefined.
    4. 对于函数定义式, 会将函数定义提前. 而函数表达式, 会在执行过程中才计算.
    5. JS的预编译是以段为处理单元的…
    作用域链和代码优化

    从作用域链的结构可以看出,在运行期上下文的作用域链中,标识符所在的位置越深,读写速度就会越慢。如上图所示,因为全局变量总是存在于运行期上下文作用域链的最末端,因此在标识符解析的时候,查找全局变量是最慢的。所以,在编写代码的时候应尽量少使用全局变量,尽可能使用局部变量。一个好的经验法则是:如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用。例如下面的代码:

    function changeColor(){
        document.getElementById("btnChange").onclick=function(){
            document.getElementById("targetCanvas").style.backgroundColor="red";
        };
    }
    

    这个函数引用了两次全局变量document,查找该变量必须遍历整个作用域链,直到最后在全局对象中才能找到。这段代码可以重写如下:

    function changeColor(){
        var doc=document;
        doc.getElementById("btnChange").onclick=function(){
            doc.getElementById("targetCanvas").style.backgroundColor="red";
        };
    }
    

    这段代码比较简单,重写后不会显示出巨大的性能提升,但是如果程序中有大量的全局变量被从反复访问,那么重写后的代码性能会有显著改善。

    改变作用域链

    有两种方法可以改变作用域链:

    1. with:应该避免,使作用域链更长,有性能影响;
    with的作用域链
    1. try-catch语句中的catch语句:
        try{
        doSomething();
    }catch(ex){
        alert(ex.message); //作用域链在此处改变
    }
    

    一旦catch语句执行完毕,作用域链机会返回到之前的状态。
    try-catch语句在代码调试和异常处理中非常有用,因此不建议完全避免。

    参考文献

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

    相关文章

      网友评论

        本文标题:JavaScript作用域学习笔记

        本文链接:https://www.haomeiwen.com/subject/cqzxlttx.html