this 关键字学习笔记(二)

this会根据运行时当前运行环境的不同指向不同的对象

var obj = {
            color:'black',
            foo: function(){
                console.log(this);
            }
        }

        var obj2 = {
            color:'white'
        }
        //在obj的环境下调用foo,this指向obj
        obj.foo();
        
        //把obj.foo函数内存地址赋值给obj2.foo
        obj2.foo = obj.foo;

        //在obj2环境下调用foo,this指向obj2
        obj2.foo();

在这里插入图片描述
由于this指向的不确定性,我们使用时候应该尽量避免使用多层this:

var o = {
            f1: function () {
                console.log(this);
                var f2 = function () {
                    console.log(this);
                }();
            }
        }
        o.f1()

因为我们知道,对象中可以保存一般的属性或者是一个函数,如果是一般的属性,那么属性的值保存在属性描述对象的value属性里面;而如果是一个函数,那么引擎会将函数单独保存在内存中,然后再将函数的地址赋值给属性的value属性,所以这段代码相当于执行了:

        var temp = function () {
            console.log(this);
        }

        var obj = {
            f1: function () {
                console.log(this);
                var f2 = temp();
            }
        }
        obj.f1()

所以第一次log this时候this指向的是对象obj,而f1函数里面有个立即执行函数f2,它的this是指向顶层对象window的,所以第二次log的this指的是window:
在这里插入图片描述
解决办法:就是把当前的this指向的对象地址保存在一个变量self里面,然后内层函数想要调用的时候就直接调用self:

var obj = {
            f1: function () {
                console.log(this);
                var self = this;
                var f2 = function(){
                    console.log(self);
                }();
            }
        }
        obj.f1()

结果:
在这里插入图片描述
这时候在f2内部调用self,实际指向的就是外面的obj,不会发生this指向的改变带来的不确定性问题

总结一下,这个方法的原理就是使用一个变量固定this的值,然后内层函数调用这个变量

还有一个需要注意的地方:
JavaScript 提供了严格模式,也可以硬性避免这种问题。严格模式下,如果函数内部的this指向顶层对象,就会报错。

var obj = {
            count: 0
        };
        obj.inc = function () {
            'use strict';
            this.count++
        };
        var f = obj.inc;
        f();

在这里插入图片描述
上面代码中,inc方法通过声明use strict采用严格模式,这时内部的this一旦指向顶层对象,就会报错。
解决办法之一是使用call()方法改变inc函数内部的指向,让它指回obj:

        var obj = {
            count: 0
        };
        obj.inc = function () {
            'use strict';
            this.count++
        };
        var f = obj.inc;
        f.call(obj);

这样就不会报错了,具体细节我们后面再讨论。

我们再看一个例子,这个例子说明了应该尽量避免数组处理方法中的 this
数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。

var obj = {
            v: 'hello',
            p: ['a1', 'a2'],
            f: function f() {
                this.p.forEach(function (item) {
                    // 内层函数的this指向的是顶层对象window
                    console.log(this.v + ' ' + item);
                });
            }
        }
        obj.f()

因为内层函数的this指向的是顶层对象window,自然读取不到window.v,所以返回的是undefined,拼接在一起就是undefined a1和undefined a2
在这里插入图片描述
要解决这个问题,常见的两个办法:
方法1:使用中间变量固定this

        var obj = {
            v: 'hello',
            p: ['a1', 'a2'],
            f: function f() {
            	//使用中间变量self固定this
                var self = this;
                this.p.forEach(function (item) {
                    // 这里的self指向的是obj对象本身
                    console.log(self.v + ' ' + item);
                });
            }
        }
        obj.f()

方法2:给forEach方法传入thisArg参数:

        var obj = {
            v: 'hello',
            p: ['a1', 'a2'],
            f: function f() {
                this.p.forEach(function (item) {
                    console.log(this.v + ' ' + item);
                },this);
                // ^---- 注意这里我们传递的this指向的就是obj,每次调用forEach里面方法的时候,this都指向obj对象
            }
        }
        obj.f()

输出:
在这里插入图片描述

所以我们得到一个重要的结论:内层函数的this不指向外部,而指向顶层对象window。

如果还是不明白,我们看一个MDN的例子,我改进了一下,结果可以看的更详细:
我们按照每个数组中的元素值,更新一个对象的属性:

        function Counter() {
            this.sum = 0;
            this.count = 0;
        }
        Counter.prototype.add = function (array) {
            console.log('enter function add, this --->' + this);
            array.forEach(function (entry) {
                console.log('enter function forEach, this --->' + this);
                this.sum += entry;
                ++this.count;
            });
            // ^---- Note这里啥都没
        };

        const obj = new Counter();
        obj.add([2, 5, 9]);
        console.log(obj.sum);
        console.log(obj.count);

按照设想,应该会在obj.add([2,5,9])以后,函数内部遍历这个[2,5,9]数组,每次遍历对Counter的实例obj里面的sum属性加上当前遍历到的数组元素的值,然后obj.count的值每次+1,所以最终的结果应该是打印出:
obj.sum=2+5+9=16,
obj.count=1+1+1=3
实际运行结果:
注意红框内容
为什么会这样呢,就是因为forEach函数内部的function匿名函数中的this指向的是window对象

解决办法:将this当作foreach方法的第二个参数,固定它的运行环境。
在这里插入图片描述
这样就可以了,运行结果
在这里插入图片描述

本文参考了以下资料:
网道 / 面向对象编程 / this 关键字
MDN Array.prototype.forEach()

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值