改变函数中this指向的方法
结合 MDN 官网中 JavaScript 章节中对this详解,我们了解到了函数原型对象中改变this指向的三种方法,分别为bind、apply、call,在以下的内容中会进行详细的介绍。
一、对this的理解
在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。ES5 引入了 bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的。ES2015 引入了箭头函数,ES2015 引入了箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。代码如下所示:
// 普通函数
const test1 = {
prop: 42,
func: function() {
return this.prop;
},
};
console.log(test1.func()); // expected output: 42
// 箭头函数
const test2 = {
prop: 42,
func: ()=> {
console.log(this.prop)
},
}
console.log(test.func());// expected output: undefined
二、为什么需要改变这个 this 的指向?
需要改变这个 this 的指向,是因为原来的 this 被污染了,需要重新再进行 this 指向,因为,this 指向的是被调用的父级作用域,而如果函数在另一个函数里面执行的时候,那么,这个 this 的指向的就是这个函数,而不是那个被执行函数原来的那个作用域。如下代码所示:
const fighter = {
fire: function (res) {
console.log(this.model);
},
};
setTimeout(function () {
fighter.fire();
}, 1000);
// undefined
此时,setTimeout() 里面的函数 this 的指向是指向了 window,而 fighter.fire() 这个函数的执行 this是需要指向这个 fighter 的。所以,执行的时候控制台就会输出 undefined 。因为在 fighter 这个域下找不到这个 model 的参数。这个就需要把这个 this 的指向改成指向有nodel变量的对象中。以下是改变 this 指向的方法。
三、使用 call 改变 this 指向
函数(Function)的原型对象中有一个call方法,调用call方法可以传递多个参数,但是第一个参数是改变this指向的目标对象,剩下的参数都会传递到调用call方法的函数,并且call方法调用也相当于call方法前面的函数被调用了。代码如下所示:
const fighter = {
fire: function (args1, args2) {
console.log(this.model);
console.log(args1);
console.log(args2);
},
};
setTimeout(function () {
fighter.fire.call({model:'700'}, 'fire', '700');
}, 1000);
// 700
// fire
// 700
四、使用 apply 改变 this 指向
使用 apply 改变 this 指向和 call 改变指向大致上方法是一样的,唯一有不同的就是apply方法只会只会接收两个参数如果传递超出的参数并不会返还给调用apply方法的函数,而且第二个参数规定是一个数组。代码如下所示:
const fighter = {
fire: function (args1) {
console.log(this.model);
console.log(args1);
},
};
let plane = fighter.fire;
setTimeout(function () {
plane.apply({model:'700'}, ['1']);
}, 1000);
// 700
// 1
// 如果第二个参数没有传递一个数组的话,那apply方法就会报 TypeError 类型错误;
const fighter = {
fire: function (res) {
console.log(this.model);
},
};
setTimeout(function () {
fighter.fire.apply({model:'700'},111);
}, 1000);
// VM435:7 Uncaught TypeError: CreateListFromArrayLike called on non-object at <anonymous>:7:16
五、使用 bind 改变 this 指向
bind的执行和前面的 call 和 apply 的执行是不一样的,call 和 apply 是立即执行一次。而 bind 是手动执行同时 bind 是永久改变 this 的指向。如下代码所示:
function f() {
return this.a;
}
const g = f.bind({ a: 'azerty' });
console.log(g()); // azerty
const h = g.bind({ a: 'yoo' }); // bind only works once!
console.log(h()); // azerty
const o = { a: 37, f, g, h };
console.log(o.a, o.f(), o.g(), o.h()); // 37,37, azerty, azerty
像这个例子,变量 g 是需要手动执行才会触发,这是第一点。另一点是下面再次执行 g() 的时候,由于,前面已经使用过 bind 改变了 this 的指向。这个时候再次调用 f 这个函数,就不需要再改变 this 的指向了,直接执行就可以。
六、总结
6.1「相同点」
都可以改变函数内部的this指向。
6.2「不同点」
call、apply会自动调用函数,bind需要手动调用。
call、bind有无数个参数,apply只有2个参数,且第二个参数必须为数组。
6.3「使用场景」
call 经常做继承。
apply 经常跟数组有关系。比如借助数学对象实现数组最大值最小值
bind 不调用函数,但是还想改变this指向时。比如改变定时器内部的this指向
6.4「区分call、apply」使用场景
// 1. 数组追加
var array1 = [12, "foo", {name: "Joe"}, -2458];
var array2 = ["Doe", 555, 100];
Array.prototype.push.apply(array1, array2);
console.log(array1)
// [12, "foo", {name: "Joe"}, -2458, "Doe", 555, 100]
// 2.最大值最小值
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved