一道题,就能让你完美地掌握:this指向、作用域&作用域链、闭包、特殊数据类型

inner = 'window';
function say() {
    console.log(inner);
    console.log(this.inner);
}
var obj1 = (function() {
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();
var obj2 = (function() {
    var inner = '2-1';
    return {
        inner: '2-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();

say();
obj1.say();
obj2.say();
obj1.say = say;
obj1.say();
obj1.say = obj2.say;
obj1.say();

蛮有意思的一道题,在对照答案之前,一定要先自己分析一下每一步的执行结果!

// 答案:

window
window

1-1
1-2

2-1
2-2

window
1-2

2-1
1-2

我们按照顺序一个一个来分析

   1. say();

inner = 'window';
function say() {
    console.log(inner);  // window
    console.log(this.inner);  // window
}

say();

直接为 inner 赋值,那么就相当于为 window 对象添加一个 inner 属性,并赋值

函数被直接调用,等价于由 window 来调用 say 函数,say() → window.say()。所以 say 函数体内 this 也就指向 window,也就是函数体内的 this.inner → window.inner。

直接访问 inner 属性时,先在函数体内寻找该属性,没有,就向上一层作用域寻找,在全局作用域中找到了该属性,打印。

   2. obj1.say();

var obj1 = (function() {
    console.log( '1: ', this );  // 1:  Window{window: Window,...}
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log( '2: ', this );  // 2:  {inner: '1-2', say: ƒ}

            console.log('3: ', inner);  // 3:  1-1
            console.log('4: ', this.inner);  // 4:  1-2
        }
    }
})();

obj1.say();

首先,分析 obj1,obj1 保存一个自调用的匿名函数(IIFE)IIFE的调用者是 window,所以函数体内打印的 this 自然就是一个 Window 类型的对象。

2 处打印的 this 也很容易理解,obj1 等于被 return 的对象 { inner: '1-2', say: function(){...} } ,obj1 调用 say 方法,也就相当于 { inner: '1-2', say: function(){...} } 调用 say 方法,所以打印的 this 自然也就是这个对象了。

3 处打印 inner,say 函数体中没有 inner 属性,向上层寻找,这个 “上层” ,指代的是上层作用域,只有函数可以产生局部作用域。也就是说,对象是不会产生局部作用域的,因此,say 函数体的上层作用域,其实对应的是外层的匿名函数 函数体。那么直接打印 inner,找到的也就是在匿名函数中通过 var 定义的 inner 属性。

4 处很简单,上面我们已经分析过对象中 say 函数的 this 指向,这里打印 this.inner 也就可以很直观的看出来,也就等价于 { inner: '1-2', say: function(){...} }.inner。

   3. obj2.say();

它和内部的代码和 obj1.say() 没有实质性的区别,分析方法和上面是一样的。

   4. obj1.say = say; obj1.say();

为了提升代码整体的可读性,我们对代码进行一个小小的调整

inner = 'window';
function say() {
    console.log('1: ', inner);  // 1: window
    console.log('2: ', this.inner);  // 2: 1-2
}
var obj1 = (function() {
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log('3: ', inner);
            console.log('4: ', this.inner);
        }
    }
})();

obj1.say = say;
obj1.say();

这里考察的核心,其实就只有一点,就是函数究竟属于什么类型?

如果可以肯定答案是引用类型,那么恭喜你,这道题基本已经被你攻破了!

这么一来,我们就可以确定,对 obj1.say 进行重新赋值后,通过 obj1.say() 调用的say函数,就应该是调用的全局作用域中的 say 函数。

能把这一点分析出来,基本上这道题最难的点就已经被我们拿下了。

把上面的点分析出来后,千万不能迷糊!我们是调用全局作用域中的 say 方法,并不是把全局作用域的 say 方法放到 obj1 里面。

分析出来上一点后,下面就是常规的套路,函数体内没有 inner,就去上一层作用域中查找,找到了全局作用域中的 inner。

由 obj1 调用的say方法,那么 this 就指向 obj1,this.inner 自然就是 { inner: '1-2', say: function() {...} } 对象的 inner 属性。

   5. obj1.say = obj2.say; obj1.say();

var obj1 = (function() {
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log('1: ', inner);
            console.log('2: ', this.inner);
        }
    }
})();
var obj2 = (function() {
    var inner = '2-1';
    return {
        inner: '2-2',
        say: function() {
            console.log('3: ', inner);  // 3: 2-1
            console.log('4: ', this.inner);  // 4: 1-2
        }
    }
})();

obj1.say = obj2.say;
obj1.say();

这个时候,在来看这一题,是不是就发现太简单了。

把 obj2 中的 say 方法赋给 obj1 的 say 属性,前面已经说过了,函数是引用类型。那么,在赋值后,我们通过 obj1.say() 调用的 say 方法,依旧是 obj2 的 say 方法。因此直接打印 inner 属性时,向上层寻找,找的是 obj2 指向的匿名函数体的上下文,打印的自然就是 2-1。

由于是 obj1 调用的 say 方法,那么 this 指向的也是 obj1 中被返回的对象:{ inner: '1-2', say: function() {...} },所以打印出来的自然也就是 1-2 了。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦田里的POLO桔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值