简述this,call,apply,bind之间的关系

一、什么是this?

this是JavaScript语言的一个关键字,它是函数运行时在函数体内部自动生成的一个对象,只能在函数体内部使用。函数的不同使用场合,this的指向不同。

在ES5中,this永远指向最终调用它的对象。

例1:
这里最终调用函数a的对象是全局window,相当于window.a()。
所以this指向window,this.name的值为全局变量name的值 'windowsName'。

var name = "windowsName";
function a () {
    var name = "Cherry";
    console.log(this.name); // windowsName
    console.log(this); // [object Window]
}
a();
console.log(this) // [object Window]

例2:
a.fn()调用函数fn的最终对象是a,this指向对象a,所以最终的name值是'skillnull'。
window.a.fn()调用函数fn的最终对象是a,this指向a,所以最终的name值是'skillnull'。
注:由于window下的变量和方法访问和调用的时候可以省略window,所以a.fn() === window.a.fn()。
b()调用函数fn的最终对象是window,this指向window,所以最终的name值是'windowsName'。这里将a.fn赋值给变量b的时候并没有调用fn。  

var name = "windowsName";
var a = {
    name: "skillnull",
    fn: function () {
        console.log(this.name);
    }
}
a.fn();  // skillnull
window.a.fn(); // skillnull

var b = a.fn;
b(); // windowsName

二、如何改变this的指向?

  • 使用 ES6 的箭头函数
    箭头函数的 this 始终指向函数定义时的 this,而非执行时。
    例3:
    注:若setTimeout推迟执行的函数是某个对象的方法,那么该方法中的this关键字将指向全局环境。
    由此可以看出a.fn2()中被setTimeout推迟执行的函数的最终调用对象是window,this指向window,
    而window中没有fn1方法,所以最终结果为错误信息:this.fn1 is not a function。
    a.fn3()中被setTimeout推迟执行的函数使用了箭头函数,此时的this指向函数定义时的this,即a,所以最终结果为:skillnull。
    a.fn4()中的函数没有被setTimeout推迟执行,最终对象仍为a,此时的this指向a,所以最终结果为:skillnull。
    
    var name = "windowsName";
    var a = {
        name: "skillnull",
        fn1: function () {
            console.log(this.name)
        },
        fn2: function () {
           // console.log(this) // a
            setTimeout(function () {
                // console.log(this) // [object Window]
                this.fn1()
            }, 100);
        },
        fn3: function () {
            setTimeout(() => {
               // console.log(this) // a
                this.fn1()
            }, 100);
        },
        fn4: function () {
            this.fn1()
        }
    };
    a.fn2() // this.fn1 is not a function
    a.fn3() // skillnull
    a.fn4() // skillnull
  • 在函数内部使用 that = this
    如果上面例子中的fn2中使用that = this改变一下this的指向,此时that属于fn2的内部变量,指向a,所以最终结果为:skillnull
    fn2: function () {
        var that = this;
        setTimeout(function () {
            // console.log(that) // a
            that.fn1()
        }, 100);
    }
  • new 实例化一个对象

    如果函数调用前使用了 new 关键字, 则是调用了构造函数。这看起来就像创建了新的函数,但实际上 JavaScript 函数是重新创建的对象。

    new实例化一个对象的过程如下:
    1.创建一个空对象 obj;
    2.将新创建的空对象的隐式原型指向其构造函数的显示原型。
    3.使用 call 改变 this 的指向
    4.如果无返回值或者返回一个非对象值,则将 obj 返回作为新对象;如果返回值是一个新对象的话那么直接直接返回该对象。
    伪代码表示:
    var a = new myFunction("Li", "Yafei");
    new myFunction {
        var obj = {};
        obj.__proto__ = myFunction.prototype;
        var result = myFunction.call(obj, "Li", "Yafei");
        return typeof result === 'obj' ? result : obj;
    }
  • 使用 apply、call、bind
    例4:
    call、apply、bind都可以更改this的指向,三者作用相同。
    由于a.fn赋值给全局变量b的时候没有执行,上面已经说过,此时的this指向widnow,调用b('skill', 'null')的结果为:i am skillnull
    而在调用方法b的时候使用call、apply或bind,将此时的this指向a,所以最终结果是:i am not skillnull
    
    
    var name = 'i am '
    var a = {
        name: "i am not ",
        fn: function (a, b) {
            console.log(this.name + a + b)
        }
    }
    
    var b = a.fn;
    b('skill', 'null') // i am skillnull
    b.call(a, 'skill', 'null') // i am not skillnull
    b.apply(a, ['skill', 'null']) // i am not skillnull
    b.bind(a, 'skill', 'null')() // i am not skillnull

三、call、apply、bind 和 this 的关系

      从上面的文章可以看出来,其实call、apply、bind最常用的用途是更改this指向。

四、call、apply、bind三者的区别

       call和apply不同之处主要在于参数的形式,call参数是一个列表,apply参数是一个数组。
       而bind会创建一个新函数,需要手动调用,bind的参数形式和call相同。

注:如果当前函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象)。

原文地址:https://www.cnblogs.com/Man-Dream-Necessary/p/9995784.html