如果你是个js的初学者,当决定使用js来开创一个新世界的时候,你会突然遇到很多摸不到头脑的问题,比如继承,块作用域,或者现在的主题,this关键词。js中的this到底是什么?我们为什么每次使用它的时候,都会忘记它的使用场景和环境。有时候当我们尝试用之前的经验来处理的时候发现,what?为什么跟我的设想不一致?那么好,现在你就可以花费你打一把游戏的时间,来跟随我看一下,this到底是什么。
js的内存包括两个部分,执行栈和内存堆。js运行时会维护一系列的栈,当前的栈总是处在内存栈的栈顶,所以this变量就会随着栈的操作(pop/push)而改变指向的对象。

  1. 普通函数
    默认的执行栈是全局的,如果一段代码在普通函数中调用,this会指向默认的全局对象,window或者global,视运行环境而定。当然,如果我们在代码中启用了严格模式use stirct;,那么在严格模式下,this指向的是undefined。可以参考以下的示例:

    function normalFunc() {
        console.log(this);
        console.log(this === window); // or global
    }
    function useStrictNormalFunc() {
        'use strict';
        console.log(this);
        console.log(this === window); // or global
    }
  2. 构造函数
    js中的构造函数,其实和普通函数没啥区别,除了它可以使用new来实例化(但是普通函数也可以使用new的啊)。哦还有一个区别,构造函数首字母是大写(当然没有任何规定,但是如果你小写,那肯定会带来惊喜的)。

    function Person(name) {
        this.name = name;
    }
    const person = new Person("fengxh");
    // person.name = "fengxh";

    当我们使用new来实例化的时候,此时构造函数内的this指向的是实例对象,实例过程中为实例创造了多个属性来达到我们创建构造函数的目的,这样person其实就是被构建出来的实例。除非我们手动为Person返回一个对象类型数据,否则Person默认返回的就是this(如果手动return基础数据,则也会被默认返回this)。

  3. 对象的方法调用

    function normalFunc() {
        console.log(this);
    }
    const person = {
        normalFunc
    };
    normalFunc(); // window or global or undefined
    person.normalFunc(); // yes, it's person. no!! why person

    对象的方法调用,this会指向对象本身,而不是全局变量。那么我们接着再看。

    const myFunc = person.normalFunc;
    myFunc(); // window or global

    哈哈,这里因为myFunc是普通的函数调用,所以this还是指向到全局变量,而不是person了。那么,我们再接着看。

    const person = {
        normalFunc: function() {
            console.log(this); // person
            function innerFunc() {
                console.log(this); // window or global
            }
            innerFunc();
        }
    };
    

    这里innerFunc还是作为一个普通函数被调用,所以this指向为全局变量。当然这里的函数定义都是普通定义,如果遇到箭头函数,那么情况又会不一样,这种情况我们随后再说。

  4. call,apply和bind

    const a = {
        name: "a"
    };
    const b = {
        name: "b"
    };
    function log() {
      console.log(this.name); 
    }
    log.call(a); // "a"
    log.call(b); // "b"
    log(); // undefined

    如果我们在执行时改变this的绑定,那么会产生意外的惊喜,函数中的this会被绑定到我们指向的那个对象,而不是全局变量window。Interesting。call和apply都可以动态改变函数中的this指向,他们的不同点是call的参数第一个是改变this的绑定指向,第二个及以后都是作为普通函数的参数被调用,而apply的第一个参数和call一样,第二个函数则是参数数组,等同于arguments。而bind则是修改函数的this指向的对象,然后返回一个函数,不同于前两者都是直接会调用

  5. 箭头函数
    详细定义的地方请看阮大师的介绍箭头函数定义
    箭头函数是不持有this的,所以它的this是属于外层的(此处不完全精确,希望读者可以自行阅读相关文献)。而且它不能作为构造函数使用,不能被call/apply/bind处理来达到改变函数里this指向的目的,它不持有this。

好了,看完这篇文章大家有没有学习到,有没有总结出下次遇到this再怎么处理它的问题。如果大家有什么疑问,或者对文章的理解有不同之处,请指出互相学习。谢谢。


fengxh
598 声望4 粉丝