前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在 Vue 对象模块内如何使用 this 对象?

在 Vue 对象模块内如何使用 this 对象?

作者头像
LIYI
发布2019-09-02 17:41:24
2.7K0
发布2019-09-02 17:41:24
举报
文章被收录于专栏:艺述论专栏

简而答之:不使用

众所周知,js 中的 this 对象在不同作用域下指代不同的对象实例,并且在以下 4 种场景中经常会“不知所向”:

  • 在定时器(setTimeout、setInterval等)回调中
  • 在事件句柄回调中
  • 在硬件环境(CEF、iOS、Android等)中注册的回调
  • 在桢渲染函数requestAnimationFrame的回调中

简而言之,在所有从 js 主线程之外的异步线程回调过来的函数内,this 经常会丢失。

为什么会丢失?

是因为调用代码没有将 this 对象传递过去。在 js 中所有函数或方法,其类型都是 Function,这个对象的三个方法call、apply、bind的第一个参数均是 thisArg。大多数情况下,这个 thisArg 不需要手动传递,js 解析器会根据执行上下文环境自动补全。但正由于自动补全,thisArg 有时候可能取了一个不恰当的值。

举个例子:

代码语言:javascript
复制
const USER_TOKEN_NAME = 'user-token';

function testThis(){
  setTimeout(function() {
    console.log("USER_TOKEN_NAME",this.USER_TOKEN_NAME)//undefined
  }, 0);
}
export default {
  testThis,
  USER_TOKEN_NAME,
  ...
};

在上面的代码中,this.USER_TOKEN_NAME 取不到正常的值,因为”this“并不是模块的默认输出对象。(注:在export default对象中,才能访问this.USER_TOKEN_NAME)

如何想让代码正常工作,有两种改写方法:

1)使用箭头函数

代码语言:javascript
复制
function testThis(){
  setTimeout(()=>{
    console.log("USER_TOKEN_NAME",this.USER_TOKEN_NAME)//undefined
  }, 0);
}

2)使用 bind 方法绑定 this 对象

代码语言:javascript
复制
function testThis(){
  setTimeout(()=>{
    console.log("USER_TOKEN_NAME",this.USER_TOKEN_NAME)//user-token
  }.bind(this), 0);
}

在上面代码中,bind方法会将this与Function捆绑在一个闭包中并返回这个闭包。

但是,这样使用 this 必须小心翼翼,稍有不慎就可能出现难以查找的异常。所以最好的对象模块开发规范是,不使用 this 关键字。

这里指对象模块,默认导出是一个全局的对象这种场景;如果是导出 Class,在类方法中访问类属性,是必使用 this 关键字的。

在对象模块中,所有模块内使用的变量、常量请直接在文件顶部定义,如下所示:

代码语言:javascript
复制
hasPushedStream; //是否已经开始推流

所有函数,无论最终导出、还是不导出,都直接以最简单的 function 方式声明,如下所示:

代码语言:javascript
复制
// 开启视频头
function startPreview() {
  util.trydo(_ => {
    // videoIsOpen1 = !videoIsOpen1;
    if (window.Chinook) {
      window.Chinook.startPreview(getCoreId(), '');
      videoIsOpen = true;
    }
  }, this);
}
export default {
  startPreview,
  ...
}

在上面代码中,startPreview作为导出的对象模块的外露方法,可以这样链式调用:

代码语言:javascript
复制
api.cef.startPreview()

在startPreview函数内部,访问 videoIsOpen 不需要 this 关键字。即使setTimeout回调函数不是箭头函数,只要没有使用 this 关键字,videoIsOpen变量仍然可以找到。在 js 作用域链中,如果当前作用域找不到标识符,会自动向上一级作用域查找。前提是没有使用作用域限定符 this。

如果在export default的对象中,添加了一个 videoIsOpen 默认输出,如下所示:

代码语言:javascript
复制
export default {
  videoIsOpen,
  startPreview,
  ...
}

这个时候,在 startPreview 函数内使用videoIsOpen、还是this.videoIsOpen,都可以正常访问。但访问的却不是同一个变量。如果不清楚这个差别,可能程序会出现让人抓狂的 bug,但就是不知道错误在哪里。

对象模块维护自身状态,原则上它不需要、也不能向外暴露自己的私有变量。如果外界模块需要这个对象的一个只读属性,怎么办?

可以这样:

代码语言:javascript
复制
export default {
  get videoIsOpen() {
    return videoIsOpen;
  },
  ...
}

虽然 getter 名称与变量名相同,但不会混淆。在外界使用 api.cef.videoIsOpen 这样的方式访问只读属性,在模块文件内部,直接使用 videoIsOpen 读写变量。访问的是同一个标识符。

Q/A

在回调中如何保证 this 对象的正确指向?

使用bind方法,在上面已经使用过了。附一个trydo函数示例:

代码语言:javascript
复制
/*
 * example:
 * `Util.trydo((a,b)=>{
 * console.log('trydo func',a,b)
 * },this,1,2)`
 * 如果要在f内使用this对象,第二个参数一定要传递
 */
function trydo(f, thisArg, ...args) {
  try {
    f.bind(thisArg, ...args).apply(thisArg, args);
  } catch (error) {
    console.log('try error', error);
    return false;
  }
  return true;
}

以上,不知道我讲明白没有。

2019/04/10 石桥码农

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 艺述论 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档