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


Posted in Javascript onSeptember 16, 2019

我喜欢在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),它仅执行作为参数提供的函数。

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

execute(function() { return 10 }); // => 10

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

1.方法分离问题

假设有一个类Person包含字段firstName和lastName。此外,它还有一个方法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 这种冗长方式,也可以使用简洁方便的胖箭头表示方法。

原文:https://github.com/valentinogagliardi/Little-JavaScript-Book/blob/v1.0.0/manuscript/chapter5.md

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
html文件中jquery与velocity变量中的$冲突的解决方法
Nov 01 Javascript
js继承call()和apply()方法总结
Dec 08 Javascript
JavaScript 浏览器对象模型BOM使用介绍
Apr 13 Javascript
javascript+canvas实现刮刮卡抽奖效果
Jul 29 Javascript
jQuery中delegate()方法的用法详解
Oct 13 Javascript
javascript基础知识讲解
Jan 11 Javascript
微信小程序textarea层级过高(盖住其他元素)问题的解决办法
Mar 04 Javascript
利用weixin-java-miniapp生成小程序码并直接返回图片文件流的方法
Mar 29 Javascript
js中Generator函数的深入讲解
Apr 07 Javascript
详解微信小程序回到顶部的两种方式
May 09 Javascript
mpvue实现左侧导航与右侧内容的联动
Oct 21 Javascript
vue视频播放插件vue-video-player的具体使用方法
Nov 08 Javascript
使用xampp将angular项目运行在web服务器的教程
Sep 16 #Javascript
在layui.use 中自定义 function 的正确方法
Sep 16 #Javascript
使用VUE实现在table中文字信息超过5个隐藏鼠标移到时弹窗显示全部
Sep 16 #Javascript
js实现无限瀑布流实例方法
Sep 16 #Javascript
策略模式实现 Vue 动态表单验证的方法
Sep 16 #Javascript
jQuery设置下拉框显示与隐藏效果的方法分析
Sep 15 #jQuery
Vue实现滑动拼图验证码功能
Sep 15 #Javascript
You might like
社区(php&amp;&amp;mysql)三
2006/10/09 PHP
php使用CURL不依赖COOKIEJAR获取COOKIE的方法
2015/06/17 PHP
Jquery 1.42 checkbox 全选和反选代码
2010/03/27 Javascript
JavaScript中使用正则匹配多条,且获取每条中的分组数据
2010/11/30 Javascript
用jQuery实现一些导航条切换,显示隐藏的实例代码
2013/06/08 Javascript
nodejs实现黑名单中间件设计
2014/06/17 NodeJs
jQuery实现当前页面标签高亮显示的方法
2015/03/10 Javascript
JavaScript实现清空(重置)文件类型INPUT元素值的方法
2016/11/17 Javascript
原生js实现键盘控制div移动且解决停顿问题
2016/12/05 Javascript
jquery对象与DOM对象转化
2017/02/08 Javascript
jquery使用EasyUI Tree异步加载JSON数据(生成树)
2017/02/11 Javascript
一篇文章让你彻底弄懂JS的事件冒泡和事件捕获
2017/08/14 Javascript
基于js的变量提升和函数提升(详解)
2017/09/17 Javascript
js+css3实现炫酷时钟
2020/08/18 Javascript
Python中用altzone()方法处理时区的教程
2015/05/22 Python
解决Python requests 报错方法集锦
2017/03/19 Python
python常用函数与用法示例
2019/07/02 Python
python监控进程状态,记录重启时间及进程号的实例
2019/07/15 Python
python多线程同步实例教程
2019/08/11 Python
基于python的selenium两种文件上传操作实现详解
2019/09/19 Python
pycharm显示远程图片的实现
2019/11/04 Python
python字典key不能是可以是啥类型
2020/08/04 Python
python爬虫基础之urllib的使用
2020/12/31 Python
Python3.9.1中使用match方法详解
2021/02/08 Python
Notino芬兰:购买香水和化妆品
2019/04/15 全球购物
哈曼俄罗斯官方网上商店:Harman.club
2020/07/24 全球购物
请写出char *p与"零值"比较的if语句
2014/09/24 面试题
微信营销策划方案
2014/02/24 职场文书
商业项目策划方案
2014/06/05 职场文书
课程设计的心得体会
2014/09/03 职场文书
高中生自我评价范文2015
2015/03/03 职场文书
爱的教育读书笔记
2015/06/26 职场文书
入党后的感想
2015/08/10 职场文书
某某幼儿园的教育教学管理调研分析报告
2019/11/29 职场文书
详解NumPy中的线性关系与数据修剪压缩
2022/05/25 Python
java实现web实时消息推送的七种方案
2022/07/23 Java/Android