zoukankan      html  css  js  c++  java
  • 函数作用域和作用域链

    作用域

    所谓作用域就是:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。

    function scope(){
        var foo = "global";
        if(window.getComputedStyle){
            var a = "I'm if";
            console.log("if:"+foo); //if:global
        }
        while(1){
            var b = "I'm while";
            console.log("while:"+foo);//while:global
            break;
        }
        !function (){
            var c = "I'm function";
            console.log("function:"+foo);//function:global
        }();
        console.log(
             foo,//global
             a, // I'm if
             b, // I'm while
             c  // c is not defined
        );
    }
    scope();
    • scope函数中定义的foo变量,除过自身可以访问以外,还可以在if语句、while语句和内嵌的匿名函数中访问。 因此,foo的作用域就是scope函数体。
    • 在javascript中,if、while、for 等代码块不能形成独立的作用域。因此,javascript中没有块级作用域,只有函数作用域。

     但是,在JS中有一种特殊情况:

     如果一个变量没有使用var声明,window便拥有了该属性,因此这个变量的作用域不属于某一个函数体,而是window对象。

    function varscope(){
        foo = "I'm in function";
        console.log(foo);//I'm in function
    }
    varscope();
    console.log(window.foo); //I'm in function

    作用域链

     所谓作用域链就是:一个函数体中嵌套了多层函数体,并在不同的函数体中定义了同一变量, 当其中一个函数访问这个变量时,便会形成一条作用域链。

    foo = "window";
    function first(){
        var foo = "first";
        function second(){
           var foo = "second";
           console.log(foo);
        }
        function third(){
           console.log(foo);
        }
        second(); //second
        third();  //first
    }
    first();

    当执行second时,JS引擎会将second的作用域放置链表的头部,其次是first的作用域,最后是window对象,于是会形成如下作用域链:

    second->first->window,  此时,JS引擎沿着该作用域链查找变量foo, 查到的是 second

    当执行third时,third形成的作用域链:third->first->window, 因此查到的是:frist

    作用域链延长(with/catch)

    with 和 catch 语句主要用来临时扩展作用域链, 将语句中传递的变量对象添加到作用域的头部。语句结束后,原作用域链恢复正常。

    //with语句
    foo = "window";
    function first(){
        var foo = "first";
        function second(){
           var foo = "second";
           console.log(foo);
        }
        function third(obj){
           console.log(foo); //first
           with (obj){
               console.log(foo); //obj
           }
           console.log(foo); //first
        }
        var obj = {foo:'obj'};
        third(obj);
    }
    first();
    
    //catch语句
    var e = new Error('a');
    try {
        throw new Error('b');
    } catch (e) {
        console.log(e.message); //b
    } 

    在执行third()时,传递了一个obj对象,obj 中有属性foo, 在执行with语句时,JS引擎将obj放置在了原链表的头部,于是形成的作用域链如下:

    obj->third->first->window, 此时查找到的foo就是obj中的foo,因此输出的是 obj

    而在with之前和之后,都是沿着原来的链表进行查找,从而说明在with语句结束后,作用域链已恢复正常。

    this 关键字

    在一个函数中,this总是指向当前函数的所有者对象,this总是在运行时才能确定其具体的指向, 也才能知道它的调用对象。

    window.name = "window";
    function f(){
        console.log(this.name);
    }
    f();//window
    
    var obj = {name:'obj'};
    f.call(obj); //obj

    在执行f()时,此时f()的调用者是window对象,因此输出 window 

    f.call(obj) 是把f()放在obj对象上执行,相当于obj.f(),此时f 中的this就是obj,所以输出的是 obj

    实战应用

    demo1:

    var foo = "window";
    var obj = {
        foo : "obj",
        getFoo : function() {
            return function() {
                return this.foo;
            };
        }
    };
    var f = obj.getFoo();
    f(); //window

    demo2:

    var foo = "window";
    var obj = {
        foo : "obj",
        getFoo : function() {
            var that = this;
            return function(){
                return that.foo;
            };
        }
    };
    var f = obj.getFoo();
    f(); //obj

    代码解析

    // demo1:
    //执行var  f = obj.getFoo()返回的是一个匿名函数,相当于:
    var f = function(){
         return this.foo;
    }
    // f() 相当于window.f(), 因此f中的this指向的是window对象,this.foo相当于window.foo, 所以f()返回"window" 
    
    // demo2:
    // 执行var f = obj.getFoo() 同样返回匿名函数,即:
    var f = function(){
         return that.foo;
    }
    // 唯一不同的是f中的this变成了that, 要知道that是哪个对象之前,先确定f的作用域链:f->getFoo->window 并在该链条上查找that,
    // 此时可以发现that指代的是getFoo中的this, getFoo中的this指向其运行时的调用者,
    // 从var f = obj.getFoo() 可知此时this指向的是obj对象,因此that.foo 就相当于obj.foo,所以f()返回 "obj"

    如对结果有疑惑,欢迎讨论!

    原创发布 @一像素  2015.12

  • 相关阅读:
    Linux学习第一天————了解root用户和基本的shell命令
    String对象常量池特性对synchronized对象的影响
    JDBC中执行SQL语句的方式
    DEVICE_ID
    Android ScrollView与RecyclerView滑动冲突问题
    Activity与intent解析
    intent初步解析
    用Intent传递数据
    代码实现
    Unity 自定义日志保存
  • 原文地址:https://www.cnblogs.com/onepixel/p/5036369.html
Copyright © 2011-2022 走看看