浅谈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自动转为JS代码
Jun 26 Javascript
详细讲解JS节点知识
Jan 31 Javascript
使用node.js 获取客户端信息代码分享
Nov 26 Javascript
js实现鼠标感应向下滑动隐藏菜单的方法
Feb 20 Javascript
深入理解JavaScript中的对象复制(Object Clone)
May 18 Javascript
第一次接触神奇的Bootstrap导航条
Aug 09 Javascript
jQuery延迟执行的实现方法
Dec 21 Javascript
JS正则表达式修饰符global(/g)用法分析
Dec 27 Javascript
jQuery插件FusionCharts实现的2D柱状图效果示例【附demo源码下载】
Mar 06 Javascript
VUE解决 v-html不能触发点击事件的问题
Oct 28 Javascript
js瀑布流布局的实现
Jun 28 Javascript
JavaScript中的几种继承方法示例
Dec 06 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
phpadmin如何导入导出大数据文件及php.ini参数修改
2013/02/18 PHP
PHP的cURL库简介及使用示例
2015/02/06 PHP
Windows平台实现PHP连接SQL Server2008的方法
2017/07/26 PHP
PHP Beanstalkd消息队列的安装与使用方法实例详解
2020/02/21 PHP
PHP 实现 WebSocket 协议原理与应用详解
2020/04/22 PHP
jQuery源码分析之jQuery.fn.each与jQuery.each用法
2015/01/23 Javascript
javascript实现PC网页里的拖拽效果
2016/03/14 Javascript
vue-cli项目中怎么使用mock数据
2017/09/27 Javascript
Vue 进入/离开动画效果
2017/12/26 Javascript
element-ui使用导航栏跳转路由的用法详解
2018/08/22 Javascript
nodejs遍历文件夹下并操作HTML/CSS/JS/PNG/JPG的方法
2018/11/01 NodeJs
微信小程序在地图选择地址并返回经纬度简单示例
2018/12/03 Javascript
记一次vue-webpack项目优化实践详解
2019/02/17 Javascript
JS获取当前时间的年月日时分秒及时间的格式化的方法
2019/12/18 Javascript
详解vue-template-admin三级路由无法缓存的解决方案
2020/03/10 Javascript
[01:04:31]DOTA2-DPC中国联赛定级赛 iG vs Magma BO3第二场 1月8日
2021/03/11 DOTA
python使用matplotlib绘图时图例显示问题的解决
2017/04/27 Python
python中Matplotlib实现绘制3D图的示例代码
2017/09/04 Python
Django管理员账号和密码忘记的完美解决方法
2018/12/06 Python
PyQt5+requests实现车票查询工具
2019/01/21 Python
对python字典过滤条件的实例详解
2019/01/22 Python
在Python3 numpy中mean和average的区别详解
2019/08/24 Python
python 实现检验33品种数据是否是正态分布
2019/12/09 Python
python 发送get请求接口详解
2020/11/17 Python
基于CSS3 animation动画属性实现轮播图效果
2017/09/12 HTML / CSS
CSS3 实现发光边框特效
2020/11/11 HTML / CSS
FOREO斐珞尔官方旗舰店:LUNA露娜洁面仪
2018/03/11 全球购物
教师师德教育的自我评价
2013/10/31 职场文书
诚信考试承诺书
2014/03/27 职场文书
《欢乐的泼水节》教学反思
2014/04/22 职场文书
营销部内勤岗位职责
2014/04/30 职场文书
安全生产计划书
2014/05/04 职场文书
2014年教师节演讲稿范文
2014/09/10 职场文书
2014会计年终工作总结
2014/12/20 职场文书
禁毒心得体会范文
2016/01/15 职场文书
检举信的写法
2019/04/10 职场文书