面對 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.pswp.cn/news/275618.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275618.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275618.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

轉:Python: threading.local是全局變量但是它的值卻在當前調用它的線程當中

原文地址:http://www.cnblogs.com/fengmk2/archive/2008/06/04/1213958.html 在threading module中,有一個非常特別的類local。一旦在主線程實例化了一個local,它會一直活在主線程中,并且又主線程啟動的子線程調用這個local實例時&#xff0c…

matlab的邊緣檢測方法,常用圖像邊緣檢測方法及Matlab研究

2011 年 2 月 15 日第 34 卷第 4 期 現代電子技術 Modern Electronics Technique Feb. 2011 Vol. 34 No. 4 常用圖像邊緣檢測方法及 Matlab 研究 韋  煒 (西安文理學院 , 陜西 西安   710065) 摘  要 :邊緣檢測在數字圖像處理中有著重要的作用。為了在實際應用中能夠選擇最佳…

單選按鈕步驟流程向導 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。…

Android 四大組件之 Activity

什么是 Activity? Activity 是 Android 的四大組件之一,是用戶操作的可視化界面,它為用戶提供了一個完成操作指令的窗口。 當我們創建完 Activity 之后,需要調用 setContentView(view) 方法來完成界面的顯示,以此來為用…

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

大家好,我是若川。今天分享一篇axios封裝的文章。學習源碼系列、面試、年度總結、JS基礎系列。作為前端開發者,每個項目基本都需要和后臺交互,目前比較流行的ajax庫就是axios了,當然也有同學選擇request插件,這個蘿卜白…

圖片有花

http://www.56.com/u50/v_NTUwMzE1NDM.html http://www.56.com/u39/v_NTUwMzE2MjA.html http://www.cnblogs.com/coffee_cn/archive/2009/11/30/1613823.html http://www.imagemagick.org/script/binary-releases.php?ImageMagickmm3e9bn5mtos6eiaelh9d4aoe4#windows 轉載于:h…

java 代碼執行el,專屬于java的漏洞——EL表達式注入

前言“FSRC經驗分享”系列文章,旨在分享焦點科技信息安全部工作過程中的經驗總結,包括但不限于漏洞分析、運營技巧、sdl推行、等保合規、自研工具等等。歡迎各位安全從業者持續關注~0x01EL簡介表達式語言(Expression Language 以下簡稱EL)是以JSTL(JavaS…

護膚產生共鳴_通過以人為本的設計編織共鳴的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…

Oracle對表名大小寫敏感嗎,讓Oracle 大小寫敏感 表名 字段名 對像名

一、解決方案1、在表名、字段名、對象名上加上雙引號&#xff0c;即可實現讓oracle大小寫區分。2、但是這又引起了另一個問題&#xff1a;在數據庫操作中&#xff0c;sql語句中相應的表名、字段名、對象名上一定要加雙引號。解決辦法是&#xff1a;使用"\"轉義。如&a…

谷歌抽屜_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_…

寫給初中級前端的高級進階指南等

大家好&#xff0c;我是若川。話不多說&#xff0c;這一次花了幾小時精心為大家挑選了20余篇好文&#xff0c;供大家閱讀學習。本文閱讀技巧&#xff0c;先粗看標題&#xff0c;感興趣可以都關注一波&#xff0c;絕對不虧。程序員成長指北考拉妹子&#xff0c;一個有趣的且樂于…