this、apply、call、bind

本文深入探讨JavaScript中this关键字的指向规则,通过多个实例演示在不同上下文中this的变化,包括函数调用、对象方法、事件处理及定时器中的表现。同时,介绍了ES6箭头函数对this处理的独特之处,以及如何通过call、apply、bind等方法改变this的指向。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

this 的指向

在 ES5 中,其实 this 的指向,始终坚持一个原理: this 永远指向最后调用它的那个对象。

例子1:

  var name = "windowsName";
    function a() {
        var name = "Cherry";

        console.log(this.name);           

        console.log("inner:" + this);    
    }
    a();
    console.log("outer:" + this)      

例子2:

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

例子3:

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

例子4:

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

例子5:

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

    var f = a.fn;
    f();

例子6:

 var name = "windowsName";

    function fn() {
        var name = 'Cherry';
        innerFunction();
        function innerFunction() {
            console.log(this.name);      
        }
    }

    fn()

如何改变this指向

  • 使用ES6箭头函数
  • 函数内部使用 that=this
  • 使用 apply、call、bind
  • new实例化对象

例子7:

    var name = "windowsName";

    var a = {
        name : "Cherry",

        func1: function () {
            console.log(this.name)     
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            },100);
        }

    };

    a.func2()     

最后调用 setTimeout 的对象是 window,但是在 window 中并没有 func1 函数。

箭头函数

ES6 的箭头函数是可以避免 ES5 中 this 的指向问题,箭头函数的 this 始终指向函数定义时的 this,而非执行时。,箭头函数需要记着这句话:“箭头函数中是没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。

例子8:

    var name = "windowsName";

    var a = {
        name : "Cherry",

        func1: function () {
            console.log(this.name)     
        },

        func2: function () {
            setTimeout( () => {
                this.func1()
            },100);
        }

    };

    a.func2()     

在函数内部使用 that = this

例子9:

    var name = "windowsName";

    var a = {

        name : "Cherry",

        func1: function () {
            console.log(this.name)     
        },

        func2: function () {
            var _this = this;
            setTimeout( function() {
                _this.func1()
            },100);
        }

    };

    a.func2()       

这里的 this 是调用 func2 的对象 a,为了防止在 func2 中的 setTimeout 被 window 调用而导致的在 setTimeout 中的 this 为 window。我们将 this(指向变量 a) 赋值给一个变量 _this,这样,在 func2 中我们使用 _this 就是指向对象 a 了。

使用 call、apply、bind

 var name = "windowsName";   
 var a = {
        name : "Cherry",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.apply(a),100); //call(a),100); //bind(a)(),100);
        }

    };

    a.func2()         

call:

function.call(thisArg, arg1, arg2, ...)

1.call 可以接受多个参数,第一个参数是this 指向的对象,之后的是函数回调所需的入参

function add(a, b) {
  return a + b;
}

add.call(this, 1, 2) // 3

2.call 方法可以指定this 的指向(即函数执行时所在的的作用域),然后再指定的作用域中,执行函数

var obj = {};
var f = function(){
return this;
};
console.log(f() === window);  // this 指向window
console.log(f.call(obj) === obj)  改变this 指向 obj

3.call 方法的参数,应该是对象obj,如果参数为空或null,undefind,则默认传参全局对象

var n = 123;
var obj = { n: 456 };

function a() {
  console.log(this.n);
}

a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456

4.call方法的一个应用是调用对象的原生方法。

var obj = {};
obj.hasOwnProperty('toString') // false

// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function () {
  return true;
};
obj.hasOwnProperty('toString') // true

Object.prototype.hasOwnProperty.call(obj, 'toString') // false

hasOwnPropertyobj对象继承的方法,如果这个方法一旦被覆盖,就不会得到正确结果。call方法可以解决这个问题,它将hasOwnProperty方法的原始定义放到obj对象上执行,这样无论obj上有没有同名方法,都不会影响结果。

apply:

apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。

func.apply(thisArg, [arg1, arg2, ...])

apply方法的第一个参数也是this所要指向的那个对象,如果设为nullundefined,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加。

function f(x, y){
  console.log(x + y);
}

f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2

f函数本来接受两个参数,使用apply方法以后,就变成可以接受一个数组作为参数

(1)找出数组最大元素

JavaScript 不提供找出数组最大元素的函数。结合使用apply方法和Math.max方法,就可以返回数组的最大元素。

var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) 

(2)将数组的空元素变为undefined

空元素与undefined的差别在于,数组的forEach方法会跳过空元素,但是不会跳过undefined。因此,遍历内部元素的时候,会得到不同的结果。

var a = ['a', , 'b'];

function print(i) {
  console.log(i);
}

a.forEach(print)
// a
// b

Array.apply(null, a).forEach(print)
// a
// undefined
// b

(3)转换类似数组的对象

另外,利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组。

Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]

注:对象必须有length属性,以及相对应的数字键。

bind:

bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。(返回一个原函数的拷贝,并拥有指定的 this 值和初始参数)

function.bind(thisArg[, arg1[, arg2[, ...]]])

如果bind方法的第一个参数是nullundefined,等于将this绑定到全局对象,函数运行时this指向顶层对象(浏览器为window)。

例1:

var d = new Date();
d.getTime() // 1481869925657

var print = d.getTime;
print() // Uncaught TypeError: this is not a Date object.

上述代码,我们将d.getTime方法赋给变量print,然后调用print就报错了。这是因为getTime方法内部的this,绑定Date对象的

实例,赋给变量print以后,内部的this已经不指向Date对象的实例了。

例2:

我们使用bind方法可以解决这个问题:

var print = d.getTime.bind(d);
print() // 1481869925657

 

例3:绑定自身对象

var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};

var func = counter.inc.bind(counter);
func();
counter.count // 1

上面代码中,counter.inc方法被赋值给变量func。这时必须用bind方法将inc内部的this,绑定到counter,否则就会出错。

例4:绑定其它对象

var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};

var obj = {
  count: 100
};
var func = counter.inc.bind(obj);
func();
obj.count // 101

上面代码中,bind方法将inc方法内部的this,绑定到obj对象。结果调用func函数以后,递增的就是obj内部的count属性。

例5 

bind方法每运行一次,就返回一个新函数,这会产生一些问题。比如,监听事件的时候,click事件绑定bind方法生成的一个匿名函数。这样会导致无法取消绑定

element.addEventListener('click', o.m.bind(o));
element.removeEventListener('click', o.m.bind(o));

正确的方法是写法:

var listener = o.m.bind(o);
element.addEventListener('click', listener);
//  ...
element.removeEventListener('click', listener);

区别:

例1

  var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }

    var b = a.fn;
    b.apply(a,[1,2])     // 3

例2 

 var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }

    var b = a.fn;
    b.call(a,1,2)  // 3

apply 和 call 的区别是 call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。

例3

    var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }

    var b = a.fn;
    b.bind(a,1,2)

bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。MDN

因此我们需手动调用

    var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }

    var b = a.fn;
    b.bind(a,1,2)()           // 3

总结

1. call 、 apply 、bind 均能改变this 指向

2. bind 每次执行产生一个新函数,call、apply 不会

3. call ,bind 接收多个参数绑定到函数,参数单一传入,apply 接收方式为数组

 

参考:

https://juejin.im/post/6844903496253177863

https://javascript.ruanyifeng.com/oop/this.html#toc7

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值