编程技术文章分享与教程

网站首页 > 技术文章 正文

面试高频:如何改变(指定)函数中的this?

hmc789 2024-11-21 15:54:27 技术文章 2 ℃

改变函数中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

标签列表
最新留言