JS 中几种轻松处理'this'指向方式

做者:Dmitri Pavlutinhtml

来源:dmitripavlutin前端

译者:前端小智git


阿里云最近在作活动,低至2折,有兴趣能够看看promotion.aliyun.com/ntms/yunpar…github


为了保证的可读性,本文采用意译而非直译。数组

我喜欢在JS中更改函数执行上下文的指向,也称为 this 指向。浏览器

例如,我们能够在类数组对象上使用数组方法:函数

const reduce = Array.prototype.reduce;

function sumArgs() {
  return reduce.call(arguments, (sum, value) => {
    return sum += value;
  });
}

sumArgs(1, 2, 3); // => 6
复制代码

另外一方面,this 很难把握。工具

我们常常会发现本身用的 this 指向不正确。下面的教你如何简单地将 this 绑定到所需的值。学习

在开始以前,我须要一个辅助函数execute(func),它仅执行做为参数提供的函数。this

function execute(func) {
  return func();
}

execute(function() { return 10 }); // => 10
复制代码

如今,继续理解围绕this错误的本质:方法分离。

1.方法分离问题

假设有一个类Person包含字段firstNamelastName。此外,它还有一个方法getFullName(),该方法返回此人的全名。以下所示:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;

  this.getFullName = function() {
    this === agent; // => true
    return `${this.firstName} ${this.lastName}`;
  }
}

const agent = new Person('前端', '小智');
agent.getFullName(); // => '前端 小智'
复制代码

能够看到Person函数做为构造函数被调用:new Person('前端', '小智')。 函数内部的 this 表示新建立的实例。

getfullname()返回此人的全名:'前端 小智'。正如预期的那样,getFullName()方法内的 this 等于agent

若是辅助函数执行agent.getFullName方法会发生什么:

execute(agent.getFullName); // => 'undefined undefined'
复制代码

执行结果不正确:'undefined undefined',这是 this 指向不正确致使的问题。

如今在getFullName() 方法中,this的值是全局对象(浏览器环境中的 window )。 this 等于 window${window.firstName} ${window.lastName} 执行结果是 'undefined undefined'

发生这种状况是由于在调用execute(agent.getFullName)时该方法与对象分离。 基本上发生的只是常规函数调用(不是方法调用):

execute(agent.getFullName); // => 'undefined undefined'

// 等价于:

const getFullNameSeparated = agent.getFullName;
execute(getFullNameSeparated); // => 'undefined undefined'
复制代码

这个就是所谓的方法从它的对象中分离出来,当方法被分离,而后执行时,this 与原始对象没有链接。

为了确保方法内部的this指向正确的对象,必须这样作

  1. 以属性访问器的形式执行方法:agent.getFullName()
  2. 或者静态地将this绑定到包含的对象(使用箭头函数、.bind()方法等)

方法分离问题,以及由此致使this指向不正确,通常会在下面的几种状况中出现:

回调

// `methodHandler()`中的`this`是全局对象
setTimeout(object.handlerMethod, 1000);
复制代码

在设置事件处理程序时

// React: `methodHandler()`中的`this`是全局对象
<button onClick={object.handlerMethod}>
  Click me
</button>
复制代码

接着介绍一些有用的方法,即若是方法与对象分离,如何使this指向所需的对象。

2. 关闭上下文

保持this指向类实例的最简单方法是使用一个额外的变量self:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;

  const self = this;

  this.getFullName = function() {
    self === agent; // => true
    return `${self.firstName} ${self.lastName}`;
  }
}

const agent = new Person('前端', '小智');

agent.getFullName();        // => '前端 小智'
execute(agent.getFullName); // => '前端 小智'
复制代码

getFullName()静态地关闭self变量,有效地对this进行手动绑定。

如今,当调用execute(agent.getFullName)时,一切工做正常,由于getFullName()方法内 this 老是指向正确的值。

3.使用箭头函数

有没有办法在没有附加变量的状况下静态绑定this? 是的,这正是箭头函数的做用。

使用箭头函数重构Person:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;

  this.getFullName = () => `${this.firstName} ${this.lastName}`;
}

const agent = new Person('前端', '小智');

agent.getFullName();        // => '前端 小智'
execute(agent.getFullName); // => '前端 小智'
复制代码

箭头函数以词法方式绑定this。 简单来讲,它使用来自其定义的外部函数this的值。

建议在须要使用外部函数上下文的全部状况下都使用箭头函数。

4. 绑定上下文

如今让我们更进一步,使用ES6中的类重构Person

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

const agent = new Person('前端', '小智');

agent.getFullName();        // => '前端 小智'
execute(agent.getFullName); // => 'undefined undefined'
复制代码

不幸的是,即便使用新的类语法,execute(agent.getFullName)仍然返回“undefined undefined”

在类的状况下,使用附加的变量self或箭头函数来修复this的指向是行不通的。

可是有一个涉及bind()方法的技巧,它将方法的上下文绑定到构造函数中:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    this.getFullName = this.getFullName.bind(this);
  }

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

const agent = new Person('前端', '小智');

agent.getFullName();        // => '前端 小智'
execute(agent.getFullName); // => '前端 小智'
复制代码

构造函数中的this.getFullName = this.getFullName.bind(this)将方法getFullName()绑定到类实例。

execute(agent.getFullName) 按预期工做,返回'前端 小智'

5. 胖箭头方法

bind 方式有点太过冗长,我们可使用胖箭头的方式:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  getFullName = () => {
    return `${this.firstName} ${this.lastName}`;
  }
}

const agent = new Person('前端', '小智');

agent.getFullName();        // => '前端 小智'
execute(agent.getFullName); // => '前端 小智'
复制代码

胖箭头方法getFullName =() =>{…}绑定到类实例,即便将方法与其对象分离。

这种方法是在类中绑定this的最有效和最简洁的方法。

6. 总结

与对象分离的方法会产生 this 指向不正确问题。静态地绑定this,能够手动使用一个附加变量self来保存正确的上下文对象。然而,更好的替代方法是使用箭头函数,其本质上是为了在词法上绑定this

在类中,可使用bind()方法手动绑定构造函数中的类方法。固然若是你不用使用 bind 这种冗长方式,也可使用简洁方便的胖箭头表示方法。

原文:github.com/valentinoga…

代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug

交流(欢迎加入群,群工做日都会发红包,互动讨论技术)

阿里云最近在作活动,低至2折,有兴趣能够看看:promotion.aliyun.com/ntms/yunpar…

干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。

github.com/qq449245884…

我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,便可看到福利,你懂的。

相关文章
相关标签/搜索