面对 this 指向丢失,尤雨溪在 Vuex 源码中是怎么处理的

1. 前言

大家好,我是若川。好久以前我有写过《面试官问系列》,旨在帮助读者提升JS基础知识,包含new、call、apply、this、继承相关知识。其中写了 面试官问:this 指向 文章。在掘金等平台收获了还算不错的反馈。

最近有小伙伴看我的 Vuex源码 文章,提到有一处this指向有点看不懂(好不容易终于有人看我的源码文章了,感动的要流泪了^_^)。于是我写篇文章答疑解惑,简单再说说 this 指向和尤大在 Vuex 源码中是怎么处理 this 指向丢失的。

2. 对象中的this指向

var person = {name: '若川',say: function(text){console.log(this.name + ', ' + text);}
}
console.log(person.name);
console.log(person.say('在写文章')); // 若川, 在写文章
var say = person.say;
say('在写文章'); // 这里的this指向就丢失了,指向window了。(非严格模式)

3. 类中的this指向

3.1 ES5

// ES5
var Person = function(){this.name = '若川';
}
Person.prototype.say = function(text){console.log(this.name + ', ' + text);
}
var person = new Person();
console.log(person.name); // 若川
console.log(person.say('在写文章'));
var say = person.say;
say('在写文章'); // 这里的this指向就丢失了,指向 window 了。

3.2 ES6

// ES6
class Person{construcor(name = '若川'){this.name = name;}say(text){console.log(`${this.name}, ${text}`);}
}
const person = new Person();
person.say('在写文章')
// 解构
const { say } = person;
say('在写文章'); // 报错 this ,因为ES6 默认启用严格模式,严格模式下指向 undefined

4. 尤大在Vuex源码中是怎么处理的

先看代码

class Store{constructor(options = {}){this._actions = Object.create(null);// bind commit and dispatch to self// 给自己 绑定 commit 和 dispatchconst store = thisconst { dispatch, commit } = this// 为何要这样绑定 ?// 说明调用commit和dispach 的 this 不一定是 store 实例// 这是确保这两个函数里的this是store实例this.dispatch = function boundDispatch (type, payload) {return dispatch.call(store, type, payload)}this.commit = function boundCommit (type, payload, options) {return commit.call(store, type, payload, options)}}dispatch(){console.log('dispatch', this);}commit(){console.log('commit', this);}
}
const store = new Store();
store.dispatch(); // 输出结果 this 是什么呢?const { dispatch, commit } = store;
dispatch(); // 输出结果 this 是什么呢?
commit();  // 输出结果 this 是什么呢?
输出结果截图

结论:非常巧妙的用了calldispatchcommit函数的this指向强制绑定到store实例对象上。如果不这么绑定就报错了。

4.1 actions 解构 store

其实Vuex源码里就有上面解构const { dispatch, commit } = store;的写法。想想我们平时是如何写actions的。actions中自定义函数的第一个参数其实就是 store 实例。

这时我们翻看下actions文档https://vuex.vuejs.org/zh/guide/actions.html

const store = new Vuex.Store({state: {count: 0},mutations: {increment (state) {state.count++}},actions: {increment (context) {context.commit('increment')}}
})

也可以用解构赋值的写法。

actions: {increment ({ commit }) {commit('increment')}
}

有了Vuex源码构造函数里的call绑定,这样this指向就被修正啦~不得不说祖师爷就是厉害。这一招,大家可以免费学走~

接着我们带着问题,为啥上文中的context就是store实例,有dispatchcommit这些方法呢。继续往下看。

4.2 为什么 actions 对象里的自定义函数 第一个参数就是 store 实例。

以下是简单源码,有缩减,感兴趣的可以看我的文章 Vuex 源码文章

class Store{construcor(){// 初始化 根模块// 并且也递归的注册所有子模块// 并且收集所有模块的 getters 放在 this._wrappedGetters 里面installModule(this, state, [], this._modules.root)}
}

接着我们看installModule函数中的遍历注册 actions 实现

function installModule (store, rootState, path, module, hot) {// 省略若干代码// 循环遍历注册 actionmodule.forEachAction((action, key) => {const type = action.root ? key : namespace + keyconst handler = action.handler || actionregisterAction(store, type, handler, local)})
}

接着看注册 actions 函数实现 registerAction

/**
* 注册 mutation
* @param {Object} store 对象
* @param {String} type 类型
* @param {Function} handler 用户自定义的函数
* @param {Object} local local 对象
*/
function registerAction (store, type, handler, local) {const entry = store._actions[type] || (store._actions[type] = [])// payload 是actions函数的第二个参数entry.push(function wrappedActionHandler (payload) {/*** 也就是为什么用户定义的actions中的函数第一个参数有*  { dispatch, commit, getters, state, rootGetters, rootState } 的原因* actions: {*    checkout ({ commit, state }, products) {*        console.log(commit, state);*    }* }*/let res = handler.call(store, {dispatch: local.dispatch,commit: local.commit,getters: local.getters,state: local.state,rootGetters: store.getters,rootState: store.state}, payload)// 源码有删减
}

比较容易发现调用顺序是 new Store() => installModule(this) => registerAction(store) => let res = handler.call(store)

其中handler 就是 用户自定义的函数,也就是对应上文的例子increment函数。store实例对象一路往下传递,到handler执行时,也是用了call函数,强制绑定了第一个参数是store实例对象。

actions: {increment ({ commit }) {commit('increment')}
}

这也就是为什么 actions 对象中的自定义函数的第一个参数是 store 对象实例了。

好啦,文章到这里就基本写完啦~相对简短一些。应该也比较好理解。

最后再总结下 this 指向

摘抄下面试官问:this 指向文章结尾。

如果要判断一个运行中函数的 this 绑定, 就需要找到这个函数的直接调用位置。找到之后 就可以顺序应用下面这四条规则来判断 this 的绑定对象。

  1. new 调用:绑定到新创建的对象,注意:显示return函数或对象,返回值不是新创建的对象,而是显式返回的函数或对象。

  2. call 或者 apply( 或者 bind) 调用:严格模式下,绑定到指定的第一个参数。非严格模式下,nullundefined,指向全局对象(浏览器中是window),其余值指向被new Object()包装的对象。

  3. 对象上的函数调用:绑定到那个对象。

  4. 普通函数调用:在严格模式下绑定到 undefined,否则绑定到全局对象。

ES6 中的箭头函数:不会使用上文的四条标准的绑定规则, 而是根据当前的词法作用域来决定this, 具体来说, 箭头函数会继承外层函数,调用的 this 绑定( 无论 this 绑定到什么),没有外层函数,则是绑定到全局对象(浏览器中是window)。这其实和 ES6 之前代码中的 self = this 机制一样。


最近组建了一个江西人的前端交流群,如果你是江西人可以加我微信 ruochuan12 私信 江西 拉你进群。


推荐阅读

我在阿里招前端,该怎么帮你(可进面试群)
我读源码的经历

在字节做前端一年后,有啥收获~
老姚浅谈:怎么学JavaScript?

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》多篇,在知乎、掘金收获超百万阅读。
从2014年起,每年都会写一篇年度总结,已经写了7篇,点击查看年度总结。
同时,活跃在知乎@若川,掘金@若川。致力于分享前端开发经验,愿景:帮助5年内前端人走向前列。

识别方二维码加我微信、长期交流学习

今日话题

略。欢迎分享、收藏、点赞、在看我的公众号文章~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275618.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单选按钮步骤流程向导 js_创建令人愉快的按钮的6个步骤

单选按钮步骤流程向导 jsThere is no modern interactive UI without buttons. They are an fundamental part of every digital solution. Learn how to improve the style of your buttons and delight users with perfect style.没有按钮,就没有现代的交互式UI。…

axios怎么封装,才能提升效率?

大家好,我是若川。今天分享一篇axios封装的文章。学习源码系列、面试、年度总结、JS基础系列。作为前端开发者,每个项目基本都需要和后台交互,目前比较流行的ajax库就是axios了,当然也有同学选择request插件,这个萝卜白…

护肤产生共鸣_通过以人为本的设计编织共鸣的20个指针

护肤产生共鸣Deep into a project right now, I can’t help but reflect on how I practice empathy in design. Human centered design means empathising with and designing for people, keeping our focus on people throughout. It is not just one stage, it is a minds…

谷歌已推送 Android Q Beta 1

开发四年只会写业务代码,分布式高并发都不会还做程序员? >>> 今日凌晨,谷歌正式推送了 Android Q 的首个 Beta 版本,Pixel 全系列手机可以尝鲜体验这款最新的系统。 据官方博客介绍,Android Q 为用户带来了…

对使用CodeSmith模板生成NHibernate的代码的分析

CodeSmith是我们常用的代码生成工具,其跟据不同的模板生成不同代码的方式能大大加快我们的项目开发,减少重复劳动。NHibernate模板就是其常用模板之一。从这里可以下载到最新的模板文件。现在最新的版本为NHibernate-v1.2.1.2125,可以生成NHi…

若川诚邀你加源码共读群,每周一起学源码

小提醒:若川视野公众号面试、源码等文章合集在菜单栏中间【源码精选】按钮,欢迎点击阅读,也可以星标我的公众号,便于查找。回复pdf,可以获取前端优质书籍。最近我创建了一个源码共读的前端交流群,希望尝试帮…

matlab 规范,matlab-代码-规范

matlab-代码-规范 1. 标识符命名原则 标识符的名字应当直观,其长度应当符合“最小长度,最大信息量”原则。 1) 非矩阵变量: 变量名应该以小写字母开头的大小写混合形式 譬如:shadowFadingTable,servingSector&#xf…

zoom视频会议官网_人性化视频会议的空间(Zoom等)

zoom视频会议官网第二部分:房间的创造力 (Part Two: The Creativity of Rooms) In Part One I shared thoughts on how virtual spaces can often leave little room to embody our most human selves. The lack of a public sphere that parallels our shared publ…

KOFLive Postmortem

为期两个月的团队项目完成了,我们的游戏也已经发布。在这个名叫KOFLive的小游戏里,我们集成了五个真人角色,每个角色有拳脚基本招数以及三个小招、一个大招,硬值、防御、集气、双人对战、人机对战、练习模式等格斗游戏的Feature基…

单调队列优化多重背包

就是按照 % 体积的余数来分组&#xff0c;每组单调队列优化。 直接上模板好了。 1 #include <bits/stdc.h>2 3 typedef long long LL;4 const int N 100010;5 6 int n, V, cnt[N], cost[N];7 LL f[2][N], val[N], p[N], top, head;8 9 inline void Max(LL &a, const…

2021年7月 虾皮、货拉拉、有赞等面经总结

大家好&#xff0c;我是若川&#xff0c;加我微信 ruochuan12 进源码交流群。今天分享一篇7月份新鲜出炉的面经&#xff0c;文章较长&#xff0c;可以收藏再看。学习源码系列、面试、年度总结、JS基础系列。本文来自作者几米阳光 投稿 原文链接&#xff1a;https://juejin.cn/p…

谷歌抽屉_Google(最终)会杀死导航抽屉吗?

谷歌抽屉A couple of months ago Google has celebrated with enthusiasm 15 years of Google Maps, one of the most used and appreciated services worldwide from the company.几个月前&#xff0c;Google热情地庆祝Google Maps诞生15周年&#xff0c;这是该公司在全球范围…

MySQL——安装

MySQL——安装 1. 下载源&#xff1a; http://repo.mysql.com/yum/mysql-8.0-community/el/7/x86_64/mysql80-community-release-el7-2.noarch.rpm 该源目前为8.0版本&#xff0c;如果需要最新请退至根目录找。 1wget http://repo.mysql.com/yum/mysql-8.0-community/el/7/x86_…

axure9控件树 rp_如何在Axure RP 9中创建分段控件

axure9控件树 rpSegmented controls are not very easy to tackle in prototyping. This is especially true when you have more than 2 segments. This article will show you how to create a segmented control with 3 segments in Axure in just 2 simple steps. The tech…

【送书-小姐姐配音】低代码平台的核心价值与优势

大家好&#xff0c;我是若川。记得点上方听小姐姐配音&#xff0c;识别下方二维码加我微信 ruochuan12&#xff0c;明天&#xff08;8月8日&#xff09;晚8点在朋友圈发动态。点赞抽3位小伙伴包邮送《实战低代码》&#xff0c;细则见动态。最近组织了源码共读活动&#xff0c;每…

sketch钢笔工具_设计工具(Sketch,Adobe XD,Figma和InVision Studio)中奇怪的一项功能

sketch钢笔工具When you build a new product that is very similar to the existing products in the market, the designers and product managers tend to do certain features different from others. Sometimes this brings a good change, sometimes worse.当您构建与市场…

Python进阶:如何将字符串常量转化为变量?

2019独角兽企业重金招聘Python工程师标准>>> 前几天&#xff0c;我们Python猫交流学习群 里的 M 同学提了个问题。这个问题挺有意思&#xff0c;经初次讨论&#xff0c;我们认为它无解。 然而&#xff0c;我认为它很有价值&#xff0c;应该继续思考怎么解决&#xf…

尤雨溪开发的 vue-devtools 如何安装,为何打开文件的功能鲜有人知?

1. 前言大家好&#xff0c;我是若川。最近组织了一次源码共读活动。每周读 200 行左右的源码。很多第一次读源码的小伙伴都感觉很有收获&#xff0c;感兴趣可以加我微信 ruochuan12&#xff0c;拉你进群学习。第一周读的是&#xff1a;据说 99% 的人不知道 vue-devtools 还能直…

sketch浮动布局_使用智能布局和调整大小在Sketch中创建更好的可重用符号

sketch浮动布局Sketch is a widely used tool for UI designs. It implemented the Sketch是用于UI设计的广泛使用的工具。 它实施了 atomic design methodology and made the workflow of UI design much more efficient. You can create a Symbol in Sketch and use it ever…

小姐姐笔记:我是如何学习简单源码拓展视野的

大家好&#xff0c;我是若川。这是我上周组织的源码共读纪年小姐姐的笔记&#xff0c;写得很好。所以分享给大家。欢迎加我微信 ruochuan12&#xff0c;进源码共读群。其他更多人的笔记可以阅读原文查看。川哥的源码解读文章&#xff1a;据说 99% 的人不知道 vue-devtools 还能…