在 JavaScript 中轻松处理 this [每日前端夜话0xD1]

我喜欢 JavaScript 中能够更改函数执行上下文(也称为 this)的特性。

例如,你可以在类似数组的对象上使用数组方法:

1const reduce = Array.prototype.reduce;23function sumArgs() {4  return reduce.call(arguments, (sum, value) => {5    return sum += value;6  });7}89sumArgs(1, 2, 3); // => 6

但是从另一方面来说,this 关键字很难掌握。

你可能会经常去检查 this 的值不正确的原因。以下各节将会教给你一些把 this绑定到所需的值简单的方法。

在开始之前,我需要一个辅助函数 execute(func)。它只是用来执行作为参数的函数:

1function execute(func) {2  return func();3}45execute(function() { return 10 }); // => 10

现在,让我们继续了解围绕 this 的错误的本质:方法分离。

1. 方法分离问题

Person 类包含字段 firstName 和 lastName。另外,它还有 getFullName()方法,返回全名。

Person 的一种可能的实现方式是:

1function Person(firstName, lastName) { 2  this.firstName = firstName; 3  this.lastName = lastName; 4 5  this.getFullName = function() { 6    this === agent; // => true 7    return `${this.firstName} ${this.lastName}`; 8  } 9}1011const agent = new Person('John', 'Smith');12agent.getFullName(); // => 'John Smith'

你会看到 Person 函数作为构造函数被调用:new Person('John','Smith')。在 Person 函数内部创建新的实例。

agent.getFullName() 返回 person 的全名:'John Smith'。不出所料,getFullName() 方法中的 this 等同于 agent。

如果帮助函数执行 help.getFullName 方法将会发生什么:

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

执行结果不正确:'undefined undefined'。这个问题是由 this 值不正确引起的。

现在,在方法 getFullName() 中,this 的值是全局对象(浏览器环境中的 window)。假设 this 等于 window,则对 ${window.firstName} ${window.lastName}的评估为 undefined undefined。

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

1execute(agent.getFullName); // => 'undefined undefined'23// is equivalent to:45const getFullNameSeparated = agent.getFullName;6execute(getFullNameSeparated); // => 'undefined undefined'

这种效果就是我所说的与对象分离的方法。当方法被分离并随后执行时,它与其原始对象没有任何关系。

1.为了确保方法中的 this 指向正确的对象,你必须:

2.以属性访问器的形式执行该方法:agent.getFullName()

或将 this 静态绑定到包含的对象(使用箭头函数,.bind() 方法等)

在方法分离问题中,返回的 this 不正确,以下面不同的形式出现:

在设置回调时

1// `this` inside `methodHandler()` is the global object2setTimeout(object.handlerMethod, 1000);

在设置事件处理程序时

1// React: `this` inside `methodHandler()` is the global object2<button onClick={object.handlerMethod}>3  Click me4</button>5

让我们继续了解一些有用的方法,来解决即使方法与对象是分开的,也能使其始终指向所需对象的问题。

2. 关闭上下文

使 this 指向类实例的最简单方法是使用附加变量 self:

1function Person(firstName, lastName) { 2  this.firstName = firstName; 3  this.lastName = lastName; 4 5  const self = this; 6 7  this.getFullName = function() { 8    self === agent; // => true 9    return `${self.firstName} ${self.lastName}`;10  }11}1213const agent = new Person('John', 'Smith');1415agent.getFullName();        // => 'John Smith'16execute(agent.getFullName); // => 'John Smith'

getFullName() 会静态关闭 self 变量,从而有效地手动绑定到 this。

现在,当调用 execute(agent.getFullName) 时返回 'John Smith',因为 getFullName()方法始终具有正确的 this 值,所以能够正常工作。

3. 使用箭头功能对 this 进行语义化

有没有一种可以在没有其他变量的情况下静态绑定 this 的方法?是的,这正是箭头函数的作用。

为了使用箭头函数,让我们重构 Person :

1function Person(firstName, lastName) { 2  this.firstName = firstName; 3  this.lastName = lastName; 4 5  this.getFullName = () => `${this.firstName} ${this.lastName}`; 6} 7 8const agent = new Person('John', 'Smith'); 910agent.getFullName();        // => 'John Smith'11execute(agent.getFullName); // => 'John Smith'

箭头函数用词法绑定 this。简而言之,它使用定义在其中的外部函数的 this 值。

我建议在所有需要使用外部函数上下文的情况下都使用箭头函数。

4.绑定上下文

让我们再向前迈出一步,并使用 ES2015 类来重构 Person。

1class Person { 2  constructor(firstName, lastName) { 3    this.firstName = firstName; 4    this.lastName = lastName; 5  } 6 7  getFullName() { 8    return `${this.firstName} ${this.lastName}`; 9  }10}1112const agent = new Person('John', 'Smith');1314agent.getFullName();        // => 'John Smith'15execute(agent.getFullName); // => 'undefined undefined'

不幸的是,即使用了新的类语法,execute(agent.getFullName) 仍会返回 'undefined undefined'。

在使用类的情况下,不能使用附加的变量 self 或箭头函数来固定 this 的值。

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

1class Person { 2  constructor(firstName, lastName) { 3    this.firstName = firstName; 4    this.lastName = lastName; 5 6    this.getFullName = this.getFullName.bind(this); 7  } 8 9  getFullName() {10    return `${this.firstName} ${this.lastName}`;11  }12}1314const agent = new Person('John', 'Smith');1516agent.getFullName();        // => 'John Smith'17execute(agent.getFullName); // => 'John Smith'

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

execute(agent.getFullName) 可以正常工作,返回 'John Smith'。

5. 胖箭头方法

上述使用手动上下文绑定的方法需要样板代码。幸运的是,仍有改进的空间。

你可以用 JavaScript 的类字段建议来定义胖箭头方法:

1class Person { 2  constructor(firstName, lastName) { 3    this.firstName = firstName; 4    this.lastName = lastName; 5  } 6 7  getFullName = () => { 8    return `${this.firstName} ${this.lastName}`; 9  }10}1112const agent = new Person('John', 'Smith');1314agent.getFullName();        // => 'John Smith'15execute(agent.getFullName); // => 'John Smith'

胖箭头函数 getFullName = () => { ... } 已绑定到类实例,即使你将方法与其对象分离开也是如此。

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

六. 结论

与对象分离的方法对 this 产生了许多误解。你应该意识到这种影响。

要静态绑定 this,你可以手动使用一个附加变量 self 来保存正确的上下文对象。但是更好的选择是使用箭头函数,它天生被设计为按词法绑定 this。

在类中,你可以使用 bind() 方法在构造函数内部手动绑定类方法。

如果你想跳过编写样板代码,那么新的 JavaScript 建议类字段会带来胖箭头方法,该方法会自动将 this 绑定到类实例。

原文:https://dmitripavlutin.com/fix-this-in-javascript/

更多相关文章

  1. JavaScript中的异步生成器函数[每日前端夜话0xC9]
  2. 输入两个整数调用puts函数,求两个整数的乘积
  3. VS中scanf等函数报错解决方法
  4. php ajax成功:函数(msg) -获取msg
  5. jQuery编程基础精华02(属性、表单过滤器,元素的each,表单选择器,子元
  6. jQuery的$.getJSON方法在IE浏览器下失效的解决方案
  7. $ .post请求中的回调函数
  8. 利用jQuery的$.event.fix函数统一浏览器event事件处理
  9. 仅当鼠标停留在元素上时,jQuery才会激活鼠标悬停函数

随机推荐

  1. Android——GridView(网格视图)相关知识总
  2. android 布局边框
  3. Good Site
  4. 升级到Android Studio 3.0出现 aapt2错误
  5. ProgressBar属性小结(转载)
  6. android 布局简括
  7. Android 异步加载图片
  8. Android中attr自定义属性详解
  9. android 注册、登录实现程序
  10. Failed to fetch URL https://dl-ssl.goo