likes
comments
collection
share

你真的弄懂this指向了吗

作者站长头像
站长
· 阅读数 15

前言

在说 this 指向之前,请观察以下代码,并说出它们的输出结果:

第 1 组:标准函数

window.color = "red";
let o = {
  color: "blue",
};
function sayColor() {
  console.log(this.color);
}

sayColor(); // 输出值是什么?

o.sayColor = sayColor;
o.sayColor(); // 输出值是什么?

第 2 组:箭头函数

window.color = "red";
let o = {
  color: "blue",
};
let sayColor = () => console.log(this.color);

sayColor(); // 输出值是什么?

o.sayColor = sayColor;
o.sayColor(); // 输出值是什么?

一、标准函数和箭头函数的 this 指向

先公布一下以上函数的输出结果,不知道屏幕前的你答对了吗?

第 1 组输出结果如下

sayColor(); // 'red'
o.sayColor(); // 'blue'

第 2 组输出结果如下

sayColor(); // 'red'
o.sayColor(); // 'red'

为什么会呈现出这样的输出结果呢?

标准函数中,this 指向的是把函数当成方法调用的上下文对象,在网页的全局上下文中调用函数时,this 指向 window,因此 this 指向是会发生改变的

箭头函数中,this 指向的是定义箭头函数的上下文对象,箭头函数在哪个对象的上下文中定义的,this 就指向谁,,箭头函数中,this 的指向是固定的

二、改变 this 指向

我们知道,函数其实是一个对象,因此它有属性和方法。每个函数都有两个属性:lengthprototype。其中 length 属性保存函数定义的命名参数的个数,prototype 属性保存引用类型所有实例和方法。callapplybind ,就是保存在 prototype 中的方法。

前面已经提到,标准函数箭头函数的默认 this 指向,但是在某些时候,我们需要改变 this默认指向,这时候我们就需要用到 callapplybind 方法,这 3 个方法都可以用来改变 this 指向,接下来我们来详细介绍这 3 个方法。

1. call

call() 方法会以指定的 this 值来调用函数,即会设置调用函数时函数体内 this 对象的值。 call() 方法接收两个参数,第 1 个参数是函数内的 this 值,第 2 个参数是调用该函数需要传入的值。第 1 个参数是必填项,第 2 个参数可以不填。

call(this, num);

以前言中的代码举例,观察 this 的变化

window.color = "red";
let o = {
  color: "blue",
};

function sayColor() {
  console.log(this.color);
}

sayColor(); // 'red'

sayColor.call(o); // 'blue'

sayColor 是一个标准函数,它的 this 指向是调用时的上下文对象,我们是在全局调用的,因此它的上下文对象应该是 window,所以 sayColor() 输出 'red' 是毫无疑问的。但是 sayColor.call(o) 为什么会输出 'blue' 呢?虽然它也是在全局上下文中调用的,但是我们利用 call() 方法,给它指定了一个新的 this 值,也就是对象 o,所以这里的 this.color 指的是对象 o 中的 color

call() 方法传值需要将参数一个一个列出来,用 ',' 分开,从第 2 个参数开始,看下面示例:

function sum(num1, num2) {
  return num1 + num2;
}
num.call(this, 10, 20); // 30

2. apply

apply() 方法和 call() 作用一样,第一个参数也是指定 this 值,但是参数需要是 Array 实例或者 arguments 对象,看下面示例:

function sum(num1, num2) {
  return num1 + num2;
}

function callSum1(num1, num2) {                 
  return sum.apply(this, arguments);
}

function callSum2(num1, num2) {
  return sum.apply(this, [num1, num2]);
}

console.log(callSum1(10, 20)); // 30
console.log(callSum2(10, 20)); // 30

从示例中可以看出,无论是传入类数组对象还是数组,可以进行正常的传值。call()apply() 真正强大的地方在于,它们可以控制函数调用上下文,可以将任意对象设置为任意函数的作用域。

3. bind

call()apply() 不同,bind() 方法会新创建一个函数实例,这个新函数的 this 值,会绑定到传给 bind() 的对象。下面来看示例:

window.color = "red";
let o = {
  color: "blue",
};

function sayColor() {
  console.log(this.color);
}
let objectSayColor = sayColor.bind(o);

objectSayColor(); // 'blue'

在这里,我们在 sayColor() 上调用了 bind() 方法,并传入了一个对象 o ,创建了一个新的函数 objectSayColor(),指定其作用域为对象 o。所以,我们在任意地方调用该函数,输出的都是字符串 'blue'

4. 三者之间的区别

通过以上的分析,我们已经知道,call()apply()bind() 都可以用来修改 this 指向。它们的区别如下:

  • call()apply() 是直接修改原函数的 this 值,不会创建新的函数
  • call() 传值需要将值一个一个列出来,用 ',' 隔开
  • apply() 只能传入数组格式或者类数组格式的值
  • bind() 会返回一个修改过 this 值 的新函数

三、总结

call() 、apply() 、bind() 是为了灵活改变函数 this 指向而发明的,他们的主要用途也是用来改变 this 指向。当然,由于这些方法本身的特性,也可以用来做些其它的事情,比如计算数组最大值、最小值等。 不过,这个用途并不广泛,因为解构赋值可以更轻松达到这个目的。

const arr = [1, 2, 3, 4, 5];

Math.max.apply(this, arr);
Math.min.apply(this, arr);

Math.max(...arr);
Math.min(...arr);
转载自:https://juejin.cn/post/7208381011608617018
评论
请登录