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