JS中不同情况下的this指向1

266 阅读21分钟

前言 

对于js来说估计代码中用的最多就是this了,尤其是现在用了vue、react框架的项目,动不动就要this.xxx,但是很多时候我们只会照着用,如果需要分析一些特殊情况的this指向就有点吃力了,那么接下来就带大家一起总结一下不同情况下的this指向。

1.普通函数

先看代码
        console.log('1', this);//window 
        function a() {
            console.log('2', this);//window 
            function b() {
                console.log('3', this);//window 
                function c() {
                    console.log('4', this);//window 
                }
                c();
            }
            let d = function () {
                console.log('5', this);//window 
            }
            b();
            d();
        }
        a();

难道就这???
先来点简单的,一点点循序渐进

只要是通过function关键字注册在window下面的函数,可以进行直接执行的,this都指向window,这里与作用域无关,为什么指向都是指向window,因为这些函数都是注册在window顶级对象中的,调用的时候相当于window.a()、window.b()这样子,所以this理所应当指向window,可能这个总结的不是很好,往下看,在对象方法中有统一总结

2.事件处理函数

先上代码

<body>
    <button id="btn">点我呀</button>
    <script>
        let btn = document.getElementById('btn');
        btn.onclick = function () {
            console.log('我被点击了');
            console.log(this);//btn     
        }
        function fn() {
            console.log('我被双击了');
            console.log(this);//btn    
        } btn.ondblclick = fn;
        let myImg = new Image();
        myImg.onload = function () {
            console.log('我加载好了');
            console.log(this);//myImg     
        }
        myImg.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQoFWP4TyRgIFLd/1GFeEOK6OABACP7joDdno9VAAAAAElFTkSuQmCC';  
    </script>
</body>

在事件处理函数中,在谁身上触发的事件处理函数,函数中this就指向谁;
btn注册了点击事件处理函数,在btn身上触发了点击事件,所以函数中的this就指向btn;
像btn的双击事件这样,处理函数是后面引用上去的,也是一样(由于函数本来就是引用类型,所以当时注册和注册后再引用没区别,都是引用);
像myImg这样不是html中的获取来元素,自己创建的元素也是一样。

其实这种this指向也比较简单,就不再赘述。

3.对象的方法

先来看一段代码

        let obj = {
            name: '孙悟空',
            skill: '七十二变',
            sayHi: function () {
                console.log('你好啊'); console.log('我叫' + this.name);//我叫孙悟空       
                console.log('我的技能是' + this.skill);//我的技能是七十二变        
                console.log(this);//obj       
            }
        }
        obj.sayHi();

记住样一句话:最后是谁调用的就指向谁

重中之重

接下来跟着我的步伐,一起来理解这句话吧

在sayHi方法调用时,因为是对象obj.(点)出来的,所以是obj在调用它,所以按照谁调用的就指向谁自然而然指向obj

这个同样适用于普通函数中,普通函数虽然省略了window直接写xxx(),但是它本义上是window.xxx(),是window在调用 所以就指向window

这个也同样适用于事件处理函数中,比如给div注册一个点击事件,div可以看做一个对象,注册事件其实是给div设置一个方法,之后这个对象调用这个方法执行,方法中的this指向这个对象,举个栗子

<body>
    <button id="btn">点我呀</button>
    <script>
        let obj = { name: 'box' };
        obj.onclick = function () {
            console.log(this);//obj     
            console.log(obj.name);//box    
        }
        obj.onclick();
    </script>
</body>

上面是不是像极了给元素注册事件,最后的obj.onclick()只不过需要我们自己触发,其实元素的事件可以看做是浏览器内部帮我执行了xxx.onclick(),这样根据最后是谁调用的就指向谁,所以指向触发的事件处理函数的元素

再举一个例子巩固一下这个概念

        function fn() {
            console.log('你好啊');
            console.log('我叫' + this.name);
            console.log('我会' + this.skill);
        }
        let obj1 = {
            name: '孙悟空',
            skill: '七十二变',
            sayHi: fn
        }
        let obj2 = {
            name: '唐僧',
            skill: '念经',
            sayHi: fn
        }
        obj1.sayHi();
        //你好啊    
        //我叫孙悟空    
        //我会七十二变       
        obj2.sayHi();
        //你好啊
        //我叫唐僧     
        //我会念经

this指向其实不是固定不变的,当同一个函数作为不同对象的方法时,函数里的this指向的是不同的对象,关键在于“谁调用”,所以请记住这句话最后是谁调用的就指向谁,

最后是谁调用的就指向谁,为什么要说是最后?

再来看一段代码

        let obj1 = {
            name: '孙悟空',
            skill: '七十二变',
            obj2: {
                name: '唐僧',
                skill: '念经',
                sayHi: function () {
                    console.log(this);//obj2     
                    console.log('我叫' + this.name);//我叫唐僧         
                    console.log('我的技能是' + this.skill);//我的技能是念经        
                }
            }
        }
        obj1.obj2.sayHi();

这个sayHi中的this指向是obj2,这个与它是.(点)了多少次点出来的无关,只看最后是谁在调用它就可以了

4.构造函数与类

1.先介绍一下构造函数的this指向,看代码

        function Create1(a, b) {
            this.name = a;
            this.skill = b;
        }
        let obj = new Create1('哪吒', '喷火');
        console.log(obj.name);//哪吒    
        console.log(obj.skill);//喷火

构造函数中的this指向的是即将被创造出来的对象

new关键字一共做了四件事情:
1.创建一个空对象
2**.将this指向这个对象**
3.执行构造函数中代码,完成对象的赋值
4.自动返回这个对象

这个应该比较好理解,生成实例对象的传统是通过构造函数,另外类也可以生成实例对象

2.下面再介绍一下类中this指向,看代码

        class Create2 {
            constructor(x, y) {
                this.name = x;
                this.skill = y;
            }
            fn1() {
                console.log('我叫' + this.name);
                console.log('我会' + this.skill);
            }
            fn2() {
                console.log('这是一个寂寞的天');
                console.log(this);
            }
            static fn3() {
                console.log('下着有些伤心的雨');
                console.log(this);
            }
        }
        let obj1 = new Create2('孙悟空', '七十二变');
        let obj2 = new Create2('唐僧', '念经');
        obj1.fn1();
        //我叫孙悟空   
        //我会七十二变    
        obj2.fn1();
        //我叫孙悟空   
        //我会七十二变      
        obj1.fn2();
        obj2.fn2();
        Create2.prototype.fn2();
        Create2.fn3();

类中三个地方的this指向,建议分开理解
可以把代码复制出来,自己运行一下试试

第一种:类中constructor构造器中的this指向
constructor相当于一个独立的构造函数,它的this指向和构造函数一样指向即将被创造出来的对象

第二种:类中的方法
类中的方法其实最后是添加到构造函数的原型对象上的,凡是类创造出来的对象包括原型对象本身都可以调用(如果不太理解可以了解下原型链的知识)
所以这里的this指向是不固定的,所以请按照前面重点强调的:最后是谁调用的就指向谁

第三种:类中的静态方法
也是按照最后是谁调用的就指向谁**,**类中的静态方法和普通方法区别是,静态方法在前面有static冠名,静态方法只能类自己访问到,所以自然而然它一般指向类它自己

感谢老铁的支持,已经把文章看到这里了

由于字数限制,请看下文

juejin.cn/post/686303…