this

this

在工作和学习的过程中,我们会一不小心的掉进this的坑中,今天我们就来把这些坑给他填满,防止以后再入坑。

我们要填this的坑,那么就要先来了解一下什么是this

this是什么呢?

this是一个关键字,被自动的定义在所有函数的作用域中。而且this的指向非常的多变,它会根据调用者的不同而不同。

this在不同环境下的指向

  • 在全局的环境中
    废话不多说,直接上代码

    console.log(this); // window
    

    所以在全局的环境下,this的指向是window。这句话看似没有问题,但是却一点不严谨,因为大家都知道,js是分为两种模式的,一种是严格模式,一种是非严格模式。非严格模式我们已经试过了,那么接下来我们在看看在严格模式下的this是什么吧。

    'use strict'
    console.log(this); // window
    

    在严格模式的全局环境下,this的指向也是window。
    但是,这只是在浏览器的环境下,要是在node的环境下呢?我们一起来看看

    console.log(this);
    
    //接下来的代码是模拟在控制台中输入(假设这段代码是写在understandThis.js文件中)
    node understandThis.js
    {}   //返回了一个空对象
    

    在node的环境下,运行一个js文件,里面的一切都是属于这个js文件的,所以this指向的是module.exports。

  • 函数内部环境
    全局环境下,我们知道了,现在就来看看在函数内,this的指向是什么吧。

    function understandThis() {
            console.log(this)
        }
    understandThis(); // window
    

    在非严格模式的函数内,this的指向是window。我们在来看看在严格模式下的是什么情况。

    'use strict'
    function understandThis() {
        console.log(this)
    }
    understandThis(); // undefined
    window.understandThis(); // window
    

    在严格模式下,返回的是undefined,为什么会这样呢?
    因为在严格模式下,你需要严格的写出调用函数的对象,不可以有任何的省略。

  • 事件调用
    接下来我们就看看,在事件调用下,this的指向吧。

    //html代码
    <div class="one">1</div>
    <div class="two">2</div>
    
    //js代码
    var one = document.getElementsByClassName('one')[0];
    var two = document.getElementsByClassName('two')[0];
    function This() {
        console.log(this);
    }
    
    one.onclick = This; //<div class="one">1</div>
    two.onclick = This; //<div class="two">2</div>
    

    谁调用了This这个函数,那么this就指向谁。在严格模式中也是一样噢~

    //html代码
    <div class="one">1</div>
    <div class="two">2</div>
    
    //js代码
    'use strict'
    var one = document.getElementsByClassName('one')[0];
    var two = document.getElementsByClassName('two')[0];
    function This() {
        console.log(this);
    }
    
    one.onclick = This; //<div class="one">1</div>
    two.onclick = This; //<div class="two">2</div>
    
  • 构造函数中

    function understandThis() {
        this.name = 'xiaoqi';
        console.log(this); // {name: "xiaoqi"}
    }
    var a = new understandThis();
    console.log(a); // {name: "xiaoqi"}
    

    构造函数的调用方式和普通函数的调用方式不同,构造函数需要用new关键字来调用。当用new来调用这个函数的时候,会发生几件事件(隐式的):

    • 在构造函数的内部创建一个名为this的空对象
    • 然后在执行this.xxx = xxx;
    • 最后,要是构造函数没有返回值的话,会默认的返回this对象;如果设置的返回值是值类型,那么还是会返回this对象,如果设置的是引用值(函数、对象、数组),那么就会返回该引用类型。null是一个例外,如果是null的话,还是返回this对象。

    不管是在非严格模式下还是严格模式下,构造函数中的this指向都是使用该构造函数创建的实例对象。

  • 对象

    var obj = {
        fn: function() {
            console.log(this);
        }
    }
    obj.fn(); // obj
    var a = obj.fn;
    a(); // window
    var b = obj.fn(); // obj
    

    this有一个特点,那就是谁调用它,那么它就指向谁。
    明白这个道理之后,我们一个个来解答

    obj.fn(); // 因为这里是obj来调用它的,所以它当然指向obj啦
    
    var a = obj.fn;
    a(); //这边在全局环境下(window)创建了一个变量a,然后把obj.fn存储到变量a里面去,但是没有执行。因为变量a是储存在window对象中的,所有a()相当于window.a(),所以最后输出的是window
    
    var b = obj.fn(); // 这边虽然也是在全局下创建了变量b,但是它存储的是obj.fn()执行之后的结果,还是obj调用了fn函数。
    
  • 箭头函数

    var obj = {
        a: 10,
        fn: function(){
            console.log(this)
            console.log(this.a);
        },
        fn1: () => {
            console.log(this)
            console.log(this.a)
        }
    }
    obj.fn(); // obj 10
    obj.fn1(); // window undefined
    

    从上面的代码可以看出来,普通函数的this还是谁调用它,那么就指向谁;而箭头函数的this却不一样,虽然是obj调用它,但是它还是指向的window。难道在箭头函数内,this是指向window的吗?我们在来看看下面的代码

    var obj = {
        b : function () {
            (() => {
                console.log(this);
            })()
        }
    }
    obj.b(); // obj
    

    这段代码中的this指向了obj,为什么会这样呢?

    因为箭头函数它本身是没有this的,它会捕获自己位置上下文的this值来作为自己的一个this值。因为这段代码中,是obj调用了b函数,让b函数里面的this指向了obj,然后被箭头函数所捕获,所以此刻箭头函数内的this也变成了obj。

  • call() 、apply() 、 bind()
    有没有什么方法,可以改变this的指向呢?当然有啦,今天他们来了!

    首先出场的是call()

    var obj = {
        b : function (a, b, c) {
            (() => {
                console.log(this, a, b, c);
            })()
        }
    }
    var box = {};
    obj.b.call(box,1 ,2 ,3); // {} 1 2 3
    

    原本this应该是obj的,但是用call改变了this的指向,现在this指向了box。我们再来看看它的兄弟appy()

    var obj = {
        b : function (a, b, c) {
            (() => {
                console.log(this, a, b, c);
            })()
        }
    }
    var box = {};
    obj.b.apply(box,[1 ,2 ,3]);   {} 1 2 3
    

    他们两个除了传参列表的不一样,其他都差不多。

    方法第一位参数剩余参数
    call()改变this指向的参数把实参按照形参的个数传进去
    apply()改变this指向的参数传入一个数组(arguments)

    现在我们再来看看bind(),它和其它两个都不一样,具体哪里不一样呢?看下面的代码

    var obj = {
        a: 1
    }
    
    var obj1 = {
        b: function() {
            console.log(this.a)
        }
    }
    obj1.b.bind(obj)(); // 1
    

    原本在obj1的作用域中是没有a这个属性的,直接执行obj1.b()会返回undefind,现在通过bind方法改变this的指向,让this指向为obj,这样输出的就是obj里面a的属性值了。而且bind返回的结果是一个函数,你需要再去执行它。

    后续我会再写一篇关于 call() 、apply() 、 bind() 的笔记,记得关注哟~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值