函数作用域和作用域链

前端菜鸟的成长之路

by @一像素

原文链接 http://www.cnblogs.com/onepixel/p/5036369.html


1、作用域

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

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();

(1)scope函数中定义的foo变量,除过自身可以访问以外,还可以在if语句、while语句和内嵌的匿名函数中访问。 因此,foo的作用域就是scope函数体。

(2)在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

2、作用域链

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

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

3、作用域的延长: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语句结束后,作用域链已恢复正常。

4、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.

5、实战应用

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

6、demo解析

//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"

by @一像素

原文链接 http://www.cnblogs.com/onepixel/p/5036369.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值