日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

初学者也能看懂的 Vue3 源码中那些实用的基础工具函数

發(fā)布時間:2023/12/9 vue 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 初学者也能看懂的 Vue3 源码中那些实用的基础工具函数 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1. 前言

大家好,我是若川。最近組織了源碼共讀活動。每周讀 200 行左右的源碼。很多第一次讀源碼的小伙伴都感覺很有收獲,感興趣可以加我微信ruochuan12,拉你進群學(xué)習(xí)。

寫相對很難的源碼,耗費了自己的時間和精力,也沒收獲多少閱讀點贊,其實是一件挺受打擊的事情。從閱讀量和讀者受益方面來看,不能促進作者持續(xù)輸出文章。

所以轉(zhuǎn)變思路,寫一些相對通俗易懂的文章。其實源碼也不是想象的那么難,至少有很多看得懂。比如工具函數(shù)。本文通過學(xué)習(xí)Vue3源碼中的工具函數(shù)模塊的源碼,學(xué)習(xí)源碼為自己所用。歌德曾說:讀一本好書,就是在和高尚的人談話。同理可得:讀源碼,也算是和作者的一種學(xué)習(xí)交流的方式。

閱讀本文,你將學(xué)到:

1.?如何學(xué)習(xí)?JavaScript?基礎(chǔ)知識,會推薦很多學(xué)習(xí)資料 2.?如何學(xué)習(xí)調(diào)試?vue?3?源碼 3.?如何學(xué)習(xí)源碼中優(yōu)秀代碼和思想,投入到自己的項目中 4.?Vue?3?源碼?shared?模塊中的幾十個實用工具函數(shù) 5.?我的一些經(jīng)驗分享

shared模塊中57個工具函數(shù),本次閱讀其中的30余個。

2. 環(huán)境準(zhǔn)備

2.1 讀開源項目 貢獻指南

打開 vue-next[1], 開源項目一般都能在 README.md 或者 .github/contributing.md[2] 找到貢獻指南。

而貢獻指南寫了很多關(guān)于參與項目開發(fā)的信息。比如怎么跑起來,項目目錄結(jié)構(gòu)是怎樣的。怎么投入開發(fā),需要哪些知識儲備等。

我們可以在 項目目錄結(jié)構(gòu)[3] 描述中,找到shared模塊。

shared: Internal utilities shared across multiple packages (especially environment-agnostic utils used by both runtime and compiler packages).

README.md 和 contributing.md 一般都是英文的。可能會難倒一部分人。其實看不懂,完全可以可以借助劃詞翻譯,整頁翻譯和百度翻譯等翻譯工具。再把英文加入后續(xù)學(xué)習(xí)計劃。

本文就是講shared模塊,對應(yīng)的文件路徑是:`vue-next/packages/shared/src/index.ts`[4]

也可以用github1s訪問,速度更快。github1s packages/shared/src/index.ts[5]

2.2 按照項目指南 打包構(gòu)建代碼

為了降低文章難度,我按照貢獻指南中方法打包把ts轉(zhuǎn)成了js。如果你需要打包,也可以參考下文打包構(gòu)建。

你需要確保 Node.js[6] 版本是 10+, 而且 yarn 的版本是 1.x Yarn 1.x[7]

你安裝的 Node.js 版本很可能是低于 10。最簡單的辦法就是去官網(wǎng)重新安裝。也可以使用 nvm等管理Node.js版本。

node?-v #?v14.16.0 #?全局安裝?yarn #?克隆項目 git?clone?https://github.com/vuejs/vue-next.git cd?vue-next#?或者克隆我的項目 git?clone?https://github.com/lxchuan12/vue-next-analysis.git cd?vue-next-analysisnpm?install?--global?yarn yarn?#?install?the?dependencies?of?the?project yarn?build

可以得到 vue-next/packages/shared/dist/shared.esm-bundler.js,文件也就是純js文件。也接下就是解釋其中的一些方法。

當(dāng)然,前面可能比較啰嗦。我可以直接講 3. 工具函數(shù)。但通過我上文的介紹,即使是初學(xué)者,都能看懂一些開源項目源碼,也許就會有一定的成就感。另外,面試問到被類似的問題或者筆試題時,你說看Vue3源碼學(xué)到的,面試官絕對對你刮目相看。

2.3 如何生成 sourcemap 調(diào)試 vue-next 源碼

熟悉我的讀者知道,我是經(jīng)常強調(diào)生成sourcemap調(diào)試看源碼,所以順便提一下如何配置生成sourcemap,如何調(diào)試。這部分可以簡單略過,動手操作時再仔細(xì)看。

其實貢獻指南[8]里描述了。

Build with Source Maps Use the --sourcemap or -s flag to build with source maps. Note this will make the build much slower.

所以在 vue-next/package.json 追加 "dev:sourcemap": "node scripts/dev.js --sourcemap",yarn dev:sourcemap執(zhí)行,即可生成sourcemap,或者直接 build。

//?package.json {"version":?"3.2.1","scripts":?{"dev:sourcemap":?"node?scripts/dev.js?--sourcemap"} }

會在控制臺輸出類似vue-next/packages/vue/src/index.ts → packages/vue/dist/vue.global.js的信息。

其中packages/vue/dist/vue.global.js.map 就是sourcemap文件了。

我們在 Vue3官網(wǎng)找個例子,在 vue-next/examples/index.html。其內(nèi)容引入packages/vue/dist/vue.global.js。

//?vue-next/examples/index.html <script?src="../../packages/vue/dist/vue.global.js"></script> <script>const?Counter?=?{data()?{return?{counter:?0}}}Vue.createApp(Counter).mount('#counter') </script>

然后我們新建一個終端窗口,yarn serve,在瀏覽器中打開http://localhost:5000/examples/,如下圖所示,按F11等進入函數(shù),就可以愉快的調(diào)試源碼了。

vue-next-debugger

3. 工具函數(shù)

本文主要按照源碼 `vue-next/packages/shared/src/index.ts`[9] 的順序來寫。也省去了一些從外部導(dǎo)入的方法。

我們也可以通過ts文件,查看使用函數(shù)的位置。同時在VSCode運行調(diào)試JS代碼,我們比較推薦韓老師寫的code runner插件。

3.1 babelParserDefaultPlugins ?babel 解析默認(rèn)插件

/***?List?of?@babel/parser?plugins?that?are?used?for?template?expression*?transforms?and?SFC?script?transforms.?By?default?we?enable?proposals?slated*?for?ES2020.?This?will?need?to?be?updated?as?the?spec?moves?forward.*?Full?list?at?https://babeljs.io/docs/en/next/babel-parser#plugins*/ const?babelParserDefaultPlugins?=?['bigInt','optionalChaining','nullishCoalescingOperator' ];

這里就是幾個默認(rèn)插件。感興趣看英文注釋查看。

3.2 EMPTY_OBJ 空對象

const?EMPTY_OBJ?=?(process.env.NODE_ENV?!==?'production')??Object.freeze({}):?{};//?例子: //?Object.freeze?是?凍結(jié)對象 //?凍結(jié)的對象最外層無法修改。 const?EMPTY_OBJ_1?=?Object.freeze({}); EMPTY_OBJ_1.name?=?'若川'; console.log(EMPTY_OBJ_1.name);?//?undefinedconst?EMPTY_OBJ_2?=?Object.freeze({?props:?{?mp:?'若川視野'?}?}); EMPTY_OBJ_2.props.name?=?'若川'; EMPTY_OBJ_2.props2?=?'props2'; console.log(EMPTY_OBJ_2.props.name);?//?'若川' console.log(EMPTY_OBJ_2.props2);?//?undefined console.log(EMPTY_OBJ_2); /***?*?{?*??props:?{mp:?"若川視野",name:?"若川"}*?}*?*/

process.env.NODE_ENV 是 node 項目中的一個環(huán)境變量,一般定義為:development 和production。根據(jù)環(huán)境寫代碼。比如開發(fā)環(huán)境,有報錯等信息,生產(chǎn)環(huán)境則不需要這些報錯警告。

3.3 EMPTY_ARR 空數(shù)組

const?EMPTY_ARR?=?(process.env.NODE_ENV?!==?'production')???Object.freeze([])?:?[];//?例子: EMPTY_ARR.push(1)?//?報錯,也就是為啥生產(chǎn)環(huán)境還是用?[] EMPTY_ARR.length?=?3; console.log(EMPTY_ARR.length);?//?0

3.4 NOOP 空函數(shù)

const?NOOP?=?()?=>?{?};//?很多庫的源碼中都有這樣的定義函數(shù),比如?jQuery、underscore、lodash?等 //?使用場景:1. 方便判斷, 2. 方便壓縮 // 1. 比如: const?instance?=?{render:?NOOP };//?條件 const?dev?=?true; if(dev){instance.render?=?function(){console.log('render');} }//?可以用作判斷。 if(instance.render?===?NOOP){console.log('i'); } // 2. 再比如: //?方便壓縮代碼 //?如果是?function(){}?,不方便壓縮代碼

3.5 NO 永遠返回 false 的函數(shù)

/***?Always?return?false.*/ const?NO?=?()?=>?false;//?除了壓縮代碼的好處外。 //?一直返回?false

3.6 isOn 判斷字符串是不是 on 開頭,并且 on 后首字母不是小寫字母

const?onRE?=?/^on[^a-z]/; const?isOn?=?(key)?=>?onRE.test(key);//?例子: isOn('onChange');?//?true isOn('onchange');?//?false isOn('on3change');?//?true

onRE 是正則。^符號在開頭,則表示是什么開頭。而在其他地方是指非。

與之相反的是:$符合在結(jié)尾,則表示是以什么結(jié)尾。

[^a-z]是指不是a到z的小寫字母。

同時推薦一個正則在線工具。

regex101[10]

另外正則看老姚的迷你書就夠用了。

老姚:《JavaScript 正則表達式迷你書》問世了![11]

3.7 isModelListener 監(jiān)聽器

判斷字符串是不是以onUpdate:開頭

const?isModelListener?=?(key)?=>?key.startsWith('onUpdate:');//?例子: isModelListener('onUpdate:change');?//?true isModelListener('1onUpdate:change');?//?false //?startsWith?是?ES6?提供的方法

ES6入門教程:字符串的新增方法[12]

很多方法都在《ES6入門教程》中有講到,就不贅述了。

3.8 extend 繼承 合并

說合并可能更準(zhǔn)確些。

const?extend?=?Object.assign;//?例子: const?data?=?{?name:?'若川'?}; const?data2?=?extend(data,?{?mp:?'若川視野',?name:?'是若川啊'?}); console.log(data);?//?{?name:?"是若川啊",?mp:?"若川視野"?} console.log(data2);?//?{?name:?"是若川啊",?mp:?"若川視野"?} console.log(data?===?data2);?//?true

3.9 remove 移除數(shù)組的一項

const?remove?=?(arr,?el)?=>?{const?i?=?arr.indexOf(el);if?(i?>?-1)?{arr.splice(i,?1);} };//?例子: const?arr?=?[1,?2,?3]; remove(arr,?3); console.log(arr);?//?[1,?2]

splice 其實是一個很耗性能的方法。刪除數(shù)組中的一項,其他元素都要移動位置。

引申:`axios InterceptorManager` 攔截器源碼[13] 中,攔截器用數(shù)組存儲的。但實際移除攔截器時,只是把攔截器置為 null 。而不是用splice移除。最后執(zhí)行時為 null 的不執(zhí)行,同樣效果。axios 攔截器這個場景下,不得不說為性能做到了很好的考慮。

看如下 axios 攔截器代碼示例:

//?代碼有刪減 //?聲明 this.handlers?=?[];//?移除 if?(this.handlers[id])?{this.handlers[id]?=?null; }//?執(zhí)行 if?(h?!==?null)?{fn(h); }

3.10 hasOwn 是不是自己本身所擁有的屬性

const?hasOwnProperty?=?Object.prototype.hasOwnProperty; const?hasOwn?=?(val,?key)?=>?hasOwnProperty.call(val,?key);//?例子://?特別提醒:__proto__?是瀏覽器實現(xiàn)的原型寫法,后面還會用到 //?現(xiàn)在已經(jīng)有提供好幾個原型相關(guān)的API //?Object.getPrototypeOf //?Object.setPrototypeOf //?Object.isPrototypeOf// .call 則是函數(shù)里 this 顯示指定以為第一個參數(shù),并執(zhí)行函數(shù)。hasOwn({__proto__:?{?a:?1?}},?'a')?//?false hasOwn({?a:?undefined?},?'a')?//?true hasOwn({},?'a')?//?false hasOwn({},?'hasOwnProperty')?//?false hasOwn({},?'toString')?//?false //?是自己的本身擁有的屬性,不是通過原型鏈向上查找的。

對象API可以看我之前寫的一篇文章JavaScript 對象所有API解析,寫的還算全面。

3.11 isArray 判斷數(shù)組

const?isArray?=?Array.isArray;isArray([]);?//?true const?fakeArr?=?{?__proto__:?Array.prototype,?length:?0?}; isArray(fakeArr);?//?false fakeArr?instanceof?Array;?//?true //?所以?instanceof?這種情況?不準(zhǔn)確

3.12 isMap 判斷是不是 Map 對象

const?isMap?=?(val)?=>?toTypeString(val)?===?'[object?Map]';//?例子: const?map?=?new?Map(); const?o?=?{?p:?'Hello?World'?};map.set(o,?'content'); map.get(o);?//?'content' isMap(map);?//?true

ES6 提供了 Map 數(shù)據(jù)結(jié)構(gòu)。它類似于對象,也是鍵值對的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當(dāng)作鍵。也就是說,Object 結(jié)構(gòu)提供了“字符串—值”的對應(yīng),Map 結(jié)構(gòu)提供了“值—值”的對應(yīng),是一種更完善的 Hash 結(jié)構(gòu)實現(xiàn)。如果你需要“鍵值對”的數(shù)據(jù)結(jié)構(gòu),Map 比 Object 更合適。

3.13 isSet 判斷是不是 Set 對象

const?isSet?=?(val)?=>?toTypeString(val)?===?'[object?Set]';//?例子: const?set?=?new?Set(); isSet(set);?//?true

ES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set。它類似于數(shù)組,但是成員的值都是唯一的,沒有重復(fù)的值。

Set本身是一個構(gòu)造函數(shù),用來生成 Set 數(shù)據(jù)結(jié)構(gòu)。

3.14 isDate 判斷是不是 Date 對象

const?isDate?=?(val)?=>?val?instanceof?Date;//?例子: isDate(new?Date());?//?true//?`instanceof`?操作符左邊是右邊的實例。但不是很準(zhǔn),但一般夠用了。原理是根據(jù)原型鏈向上查找的。isDate({__proto__?:?new?Date());?//?true //?實際上是應(yīng)該是 Object 才對。 //?所以用 instanceof 判斷數(shù)組也不準(zhǔn)確。 //?再比如 ({__proto__:?[]?})?instanceof?Array;?//?true //?實際上是對象。 //?所以用?數(shù)組本身提供的方法 Array.isArray 是比較準(zhǔn)確的。

3.15 isFunction 判斷是不是函數(shù)

const?isFunction?=?(val)?=>?typeof?val?===?'function'; //?判斷數(shù)組有多種方法,但這個是比較常用也相對兼容性好的。

3.16 isString 判斷是不是字符串

const?isString?=?(val)?=>?typeof?val?===?'string';//?例子: isString('')?//?true

3.17 isSymbol 判斷是不是 Symbol

const?isSymbol?=?(val)?=>?typeof?val?===?'symbol';//?例子: let?s?=?Symbol();typeof?s; //?"symbol" // Symbol 是函數(shù),不需要用 new 調(diào)用。

ES6 引入了一種新的原始數(shù)據(jù)類型Symbol,表示獨一無二的值。

3.18 isObject 判斷是不是對象

const?isObject?=?(val)?=>?val?!==?null?&&?typeof?val?===?'object';//?例子: isObject(null);?//?false isObject({name:?'若川'});?//?true //?判斷不為?null?的原因是?typeof?null?其實?是?object

3.19 isPromise 判斷是不是 Promise

const?isPromise?=?(val)?=>?{return?isObject(val)?&&?isFunction(val.then)?&&?isFunction(val.catch); };//?判斷是不是Promise對象 const?p1?=?new?Promise(function(resolve,?reject){resolve('若川'); }); isPromise(p1);?//?true// promise 對于初學(xué)者來說可能比較難理解。但是重點內(nèi)容,JS異步編程,要著重掌握。 //?現(xiàn)在 web 開發(fā) Promise 和 async await 等非常常用。

可以根據(jù)文末推薦的書籍看Promise相關(guān)章節(jié)掌握。同時也推薦這本迷你書JavaScript Promise迷你書(中文版)[14]

3.20 objectToString 對象轉(zhuǎn)字符串

const?objectToString?=?Object.prototype.toString;//?對象轉(zhuǎn)字符串

3.21 toTypeString ?對象轉(zhuǎn)字符串

const?toTypeString?=?(value)?=>?objectToString.call(value);// call 是一個函數(shù),第一個參數(shù)是?執(zhí)行函數(shù)里面 this 指向。 //?通過這個能獲得?類似??"[object?String]"?其中?String?是根據(jù)類型變化的

3.22 toRawType ?對象轉(zhuǎn)字符串 截取后幾位

const?toRawType?=?(value)?=>?{//?extract?"RawType"?from?strings?like?"[object?RawType]"return?toTypeString(value).slice(8,?-1); };//?截取到 toRawType('');??'String'

可以 截取到 String Array 等這些類型

是 JS 判斷數(shù)據(jù)類型非常重要的知識點。

JS 判斷類型也有 ?typeof ,但不是很準(zhǔn)確,而且能夠識別出的不多。

這些算是基礎(chǔ)知識

mdn typeof 文檔[15],文檔比較詳細(xì),也實現(xiàn)了一個很完善的type函數(shù),本文就不贅述了。

//?typeof?返回值目前有以下8種? 'undefined' 'object' 'boolean' 'number' 'bigint' 'string' 'symobl' 'function'

3.23 isPlainObject 判斷是不是純粹的對象

const?objectToString?=?Object.prototype.toString; const?toTypeString?=?(value)?=>?objectToString.call(value); //? const?isPlainObject?=?(val)?=>?toTypeString(val)?===?'[object?Object]';//?前文中?有 isObject 判斷是不是對象了。 //?isPlainObject?這個函數(shù)在很多源碼里都有,比如?jQuery?源碼和?lodash?源碼等,具體實現(xiàn)不一樣 //?上文的?isObject([])?也是?true?,因為?type?[]?為?'object' //?而?isPlainObject([])?則是false const?Ctor?=?function(){this.name?=?'我是構(gòu)造函數(shù)'; } isPlainObject({});?//?true isPlainObject(new?Ctor());?//?true

3.24 isIntegerKey 判斷是不是數(shù)字型的字符串key值

const?isIntegerKey?=?(key)?=>?isString(key)?&&key?!==?'NaN'?&&key[0]?!==?'-'?&&''?+?parseInt(key,?10)?===?key;//?例子: isIntegerKey('a');?//?false isIntegerKey('0');?//?true isIntegerKey('011');?//?false isIntegerKey('11');?//?true //?其中 parseInt 第二個參數(shù)是進制。 //?字符串能用數(shù)組取值的形式取值。 //??還有一個?charAt?函數(shù),但不常用? 'abc'.charAt(0)?//?'a' //?charAt?與數(shù)組形式不同的是?取不到值會返回空字符串'',數(shù)組形式取值取不到則是?undefined

3.25 makeMap && isReservedProp

傳入一個以逗號分隔的字符串,生成一個 map(鍵值對),并且返回一個函數(shù)檢測 key 值在不在這個 map 中。第二個參數(shù)是小寫選項。

/***?Make?a?map?and?return?a?function?for?checking?if?a?key*?is?in?that?map.*?IMPORTANT:?all?calls?of?this?function?must?be?prefixed?with*?\/\*#\_\_PURE\_\_\*\/*?So?that?rollup?can?tree-shake?them?if?necessary.*/ function?makeMap(str,?expectsLowerCase)?{const?map?=?Object.create(null);const?list?=?str.split(',');for?(let?i?=?0;?i?<?list.length;?i++)?{map[list[i]]?=?true;}return?expectsLowerCase???val?=>?!!map[val.toLowerCase()]?:?val?=>?!!map[val]; } const?isReservedProp?=?/*#__PURE__*/?makeMap( //?the?leading?comma?is?intentional?so?empty?string?""?is?also?included ',key,ref,'?+'onVnodeBeforeMount,onVnodeMounted,'?+'onVnodeBeforeUpdate,onVnodeUpdated,'?+'onVnodeBeforeUnmount,onVnodeUnmounted');//?保留的屬性 isReservedProp('key');?//?true isReservedProp('ref');?//?true isReservedProp('onVnodeBeforeMount');?//?true isReservedProp('onVnodeMounted');?//?true isReservedProp('onVnodeBeforeUpdate');?//?true isReservedProp('onVnodeUpdated');?//?true isReservedProp('onVnodeBeforeUnmount');?//?true isReservedProp('onVnodeUnmounted');?//?true

3.26 cacheStringFunction 緩存

const?cacheStringFunction?=?(fn)?=>?{const?cache?=?Object.create(null);return?((str)?=>?{const?hit?=?cache[str];return?hit?||?(cache[str]?=?fn(str));}); };

這個函數(shù)也是和上面 MakeMap 函數(shù)類似。只不過接收參數(shù)的是函數(shù)。《JavaScript 設(shè)計模式與開發(fā)實踐》書中的第四章 JS單例模式也是類似的實現(xiàn)。

var?getSingle?=?function(fn){?//?獲取單例var?result;return?function(){return?result?||?(result?=?fn.apply(this,?arguments));} };

以下是一些正則,系統(tǒng)學(xué)習(xí)正則推薦老姚:《JavaScript 正則表達式迷你書》問世了![16],看過的都說好。所以本文不會過多描述正則相關(guān)知識點。

//?\w?是?0-9a-zA-Z_?數(shù)字?大小寫字母和下劃線組成 //?()?小括號是?分組捕獲 const?camelizeRE?=?/-(\w)/g; /***?@private*/ //?連字符?-?轉(zhuǎn)駝峰??on-click?=>?onClick const?camelize?=?cacheStringFunction((str)?=>?{return?str.replace(camelizeRE,?(_,?c)?=>?(c???c.toUpperCase()?:?'')); }); //?\B 是指?非?\b 單詞邊界。 const?hyphenateRE?=?/\B([A-Z])/g; /***?@private*/const?hyphenate?=?cacheStringFunction((str)?=>?str.replace(hyphenateRE,?'-$1').toLowerCase());//?舉例:onClick => on-click const?hyphenateResult?=?hyphenate('onClick'); console.log('hyphenateResult',?hyphenateResult);?//?'on-click'/***?@private*/ //?首字母轉(zhuǎn)大寫 const?capitalize?=?cacheStringFunction((str)?=>?str.charAt(0).toUpperCase()?+?str.slice(1)); /***?@private*/ //?click?=>?onClick const?toHandlerKey?=?cacheStringFunction((str)?=>?(str???`on${capitalize(str)}`?:?``));const?result?=?toHandlerKey('click'); console.log(result,?'result');?//?'onClick'

3.27 hasChanged 判斷是不是有變化

//?compare?whether?a?value?has?changed,?accounting?for?NaN. const?hasChanged?=?(value,?oldValue)?=>?value?!==?oldValue?&&?(value?===?value?||?oldValue?===?oldValue); //?例子: //?認(rèn)為?NaN?是不變的 hasChanged(NaN,?NaN);?//?false hasChanged(1,?1);?//?false hasChanged(1,?2);?//?true hasChanged(+0,?-0);?//?false //?Obect.is?認(rèn)為?+0?和?-0?不是同一個值 Object.is(+0,?-0);?//?false??????????? //?Object.is?認(rèn)為??NaN?和?本身?相比?是同一個值 Object.is(NaN,?NaN);?//?true //?場景 //?watch?監(jiān)測值是不是變化了//?(value?===?value?||?oldValue?===?oldValue) //?為什么會有這句?因為要判斷 NaN 。認(rèn)為 NaN 是不變的。因為 NaN === NaN 為 false

根據(jù) hasChanged 這個我們繼續(xù)來看看:Object.is API。

Object.is(value1, value2) (ES6)

該方法用來比較兩個值是否嚴(yán)格相等。它與嚴(yán)格比較運算符(===)的行為基本一致。不同之處只有兩個:一是+0不等于-0,而是 NaN 等于自身。

Object.is('若川',?'若川');?//?true Object.is({},{});?//?false Object.is(+0,?-0);?//?false +0?===?-0;?//?true Object.is(NaN,?NaN);?//?true NaN?===?NaN;?//?false

ES5可以通過以下代碼部署Object.is。

Object.defineProperty(Object,?'is',?{value:?function()?{x,?y}?{if?(x?===?y)?{//?針對+0不等于-0的情況return?x?!==?0?||?1?/?x?===?1?/?y;}//?針對?NaN的情況return?x?!==?x?&&?y?!==?y;},configurable:?true,enumerable:?false,writable:?true });

根據(jù)舉例可以說明

3.28 invokeArrayFns ?執(zhí)行數(shù)組里的函數(shù)

const?invokeArrayFns?=?(fns,?arg)?=>?{for?(let?i?=?0;?i?<?fns.length;?i++)?{fns[i](arg?"i");} };//?例子: const?arr?=?[function(val){console.log(val?+?'的博客地址是:https://lxchuan12.gitee.io');},function(val){console.log('百度搜索?若川?可以找到'?+?val);},function(val){console.log('微信搜索?若川視野?可以找到關(guān)注'?+?val);}, ] invokeArrayFns(arr,?'我');

為什么這樣寫,我們一般都是一個函數(shù)執(zhí)行就行。

數(shù)組中存放函數(shù),函數(shù)其實也算是數(shù)據(jù)。這種寫法方便統(tǒng)一執(zhí)行多個函數(shù)。

3.29 def 定義對象屬性

const?def?=?(obj,?key,?value)?=>?{Object.defineProperty(obj,?key,?{configurable:?true,enumerable:?false,value}); };

Object.defineProperty 算是一個非常重要的API。還有一個定義多個屬性的API:Object.defineProperties(obj, props) (ES5)

Object.defineProperty 涉及到比較重要的知識點。
在ES3中,除了一些內(nèi)置屬性(如:Math.PI),對象的所有的屬性在任何時候都可以被修改、插入、刪除。在ES5中,我們可以設(shè)置屬性是否可以被改變或是被刪除——在這之前,它是內(nèi)置屬性的特權(quán)。ES5中引入了屬性描述符的概念,我們可以通過它對所定義的屬性有更大的控制權(quán)。這些屬性描述符(特性)包括:

value——當(dāng)試圖獲取屬性時所返回的值。
writable——該屬性是否可寫。
enumerable——該屬性在for in循環(huán)中是否會被枚舉。
configurable——該屬性是否可被刪除。
set()——該屬性的更新操作所調(diào)用的函數(shù)。
get()——獲取屬性值時所調(diào)用的函數(shù)。

另外,數(shù)據(jù)描述符(其中屬性為:enumerable,configurable,value,writable)與存取描述符(其中屬性為enumerable,configurable,set(),get())之間是有互斥關(guān)系的。在定義了set()和get()之后,描述符會認(rèn)為存取操作已被 定義了,其中再定義value和writable會引起錯誤

以下是ES3風(fēng)格的屬性定義方式:

var?person?=?{}; person.legs?=?2;

以下是等價的ES5通過數(shù)據(jù)描述符定義屬性的方式:

var?person?=?{}; Object.defineProperty(person,?'legs',?{value:?2,writable:?true,configurable:?true,enumerable:?true });

其中, 除了value的默認(rèn)值為undefined以外,其他的默認(rèn)值都為false。這就意味著,如果想要通過這一方式定義一個可寫的屬性,必須顯示將它們設(shè)為true。或者,我們也可以通過ES5的存儲描述符來定義:

var?person?=?{}; Object.defineProperty(person,?'legs',?{set:function(v)?{return?this.value?=?v;},get:?function(v)?{return?this.value;},configurable:?true,enumerable:?true }); person.legs?=?2;

這樣一來,多了許多可以用來描述屬性的代碼,如果想要防止別人篡改我們的屬性,就必須要用到它們。此外,也不要忘了瀏覽器向后兼容ES3方面所做的考慮。例如,跟添加Array.prototype屬性不一樣,我們不能再舊版的瀏覽器中使用shim這一特性。另外,我們還可以(通過定義nonmalleable屬性),在具體行為中運用這些描述符:

var?person?=?{}; Object.defineProperty(person,?'heads',?{value:?1}); person.heads?=?0;?//?0 person.heads;?//?1??(改不了) delete?person.heads;?//?false person.heads?//?1?(刪不掉)

其他本文就不過多贅述了。更多對象 API 可以查看這篇文章JavaScript 對象所有API解析。

3.30 toNumber 轉(zhuǎn)數(shù)字

const?toNumber?=?(val)?=>?{const?n?=?parseFloat(val);return?isNaN(n)???val?:?n; };toNumber('111');?//?111 toNumber('a111');?//?'a111' parseFloat('a111');?//?NaN isNaN(NaN);?//?true

其實 isNaN 本意是判斷是不是 NaN 值,但是不準(zhǔn)確的。比如:isNaN('a') 為 true。所以 ES6 有了 Number.isNaN 這個判斷方法,為了彌補這一個API。

Number.isNaN('a')??//?false Number.isNaN(NaN);?//?true

3.31 getGlobalThis 全局對象

let?_globalThis; const?getGlobalThis?=?()?=>?{return?(_globalThis?||(_globalThis?=typeof?globalThis?!==?'undefined'??globalThis:?typeof?self?!==?'undefined'??self:?typeof?window?!==?'undefined'??window:?typeof?global?!==?'undefined'??global:?{})); };

獲取全局 this 指向。

初次執(zhí)行肯定是 _globalThis 是 undefined。所以會執(zhí)行后面的賦值語句。

如果存在 globalThis 就用 globalThis。MDN globalThis[17]

如果存在self,就用self。在 Web Worker 中不能訪問到 window 對象,但是我們卻能通過 self 訪問到 Worker 環(huán)境中的全局對象。

如果存在window,就用window。

如果存在global,就用global。Node環(huán)境下,使用global。

如果都不存在,使用空對象。可能是微信小程序環(huán)境下。

下次執(zhí)行就直接返回 _globalThis,不需要第二次繼續(xù)判斷了。這種寫法值得我們學(xué)習(xí)。

4. 最后推薦一些文章和書籍

先推薦我認(rèn)為不錯的JavaScript API的幾篇文章和幾本值得讀的書。

JavaScript字符串所有API全解密[18]

【深度長文】JavaScript數(shù)組所有API全解密[19]

正則表達式前端使用手冊[20]

老姚:《JavaScript 正則表達式迷你書》問世了![21]

JavaScript 對象所有API解析 https://lxchuan12.gitee.io/js-object-api/

MDN JavaScript[22]

《JavaScript高級程序設(shè)計》第4版[23]

《JavaScript 權(quán)威指南》第7版[24]

《JavaScript面向?qū)ο缶幊?》[25] 面向?qū)ο笾v的很詳細(xì)。

阮一峰老師:《ES6 入門教程》[26]

《現(xiàn)代 JavaScript 教程》[27]

《你不知道的JavaScript》上中卷[28]

《JavaScript 設(shè)計模式與開發(fā)實踐》[29]

我也是從小白看不懂書經(jīng)歷過來的。到現(xiàn)在寫文章分享。

我看書的方法:多本書同時看,看相同類似的章節(jié),比如函數(shù)。看完這本可能沒懂,看下一本,幾本書看下來基本就懂了,一遍沒看懂,再看幾遍,可以避免遺忘,鞏固相關(guān)章節(jié)。當(dāng)然,剛開始看書很難受,看不進。這些書大部分在微信讀書都有,如果習(xí)慣看紙質(zhì)書,那可以買來看。

這時可以看些視頻和動手練習(xí)一些簡單的項目。

比如:可以自己注冊一個github賬號,分章節(jié)小節(jié),抄寫書中的代碼,提交到github,練習(xí)了才會更有感覺。

再比如 freeCodeCamp 中文在線學(xué)習(xí)網(wǎng)站[30] 網(wǎng)站。看書是系統(tǒng)學(xué)習(xí)非常好的方法。后來我就是看源碼較多,寫文章分享出來給大家。

5. 總結(jié)

文中主要通過學(xué)習(xí) shared 模塊下的幾十個工具函數(shù),比如有:isPromise、makeMap、cacheStringFunction、invokeArrayFns、def、getGlobalThis等等。

同時還分享了vue源碼的調(diào)試技巧,推薦了一些書籍和看書籍的方法。

源碼也不是那么可怕。平常我們工作中也是經(jīng)常能使用到這些工具函數(shù)。通過學(xué)習(xí)一些簡單源碼,拓展視野的同時,還能落實到自己工作開發(fā)中,收益相對比較高。

參考資料

[1]

vue-next: https://github.com/vuejs/vue-next

[2]

.github/contributing.md: https://github.com/vuejs/vue-next/blob/master/.github/contributing.md

[3]

更多參考資料可以點擊 閱讀原文 查看

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


推薦閱讀

我在阿里招前端,該怎么幫你(可進面試群)
我讀源碼的經(jīng)歷

面對 this 指向丟失,尤雨溪在 Vuex 源碼中是怎么處理的
老姚淺談:怎么學(xué)JavaScript?

·················?若川簡介?·················

你好,我是若川,畢業(yè)于江西高校。現(xiàn)在是一名前端開發(fā)“工程師”。寫有《學(xué)習(xí)源碼整體架構(gòu)系列》多篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結(jié),已經(jīng)寫了7篇,點擊查看年度總結(jié)。
同時,活躍在知乎@若川,掘金@若川。致力于分享前端開發(fā)經(jīng)驗,愿景:幫助5年內(nèi)前端人走向前列。

識別方二維碼加我微信、拉你進源碼共讀

今日話題

略。歡迎分享、收藏、點贊、在看我的公眾號文章~

總結(jié)

以上是生活随笔為你收集整理的初学者也能看懂的 Vue3 源码中那些实用的基础工具函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。