初學者也能看懂的 Vue3 源碼中那些實用的基礎工具函數

1. 前言

你們好,我是若川。歡迎關注個人公衆號若川視野,最近組織了源碼共讀活動,感興趣的能夠加我微信 ruochuan12,長期交流學習。javascript

以前寫的《學習源碼總體架構系列》 包含jQueryunderscorelodashvuexsentryaxiosreduxkoavue-devtoolsvuex4十篇源碼文章。html

寫相對很難的源碼,耗費了本身的時間和精力,也沒收穫多少閱讀點贊,實際上是一件挺受打擊的事情。從閱讀量和讀者受益方面來看,不能促進做者持續輸出文章。前端

因此轉變思路,寫一些相對通俗易懂的文章。其實源碼也不是想象的那麼難,至少有不少看得懂。好比工具函數。本文經過學習Vue3源碼中的工具函數模塊的源碼,學習源碼爲本身所用。歌德曾說:讀一本好書,就是在和高尚的人談話。 同理可得:讀源碼,也算是和做者的一種學習交流的方式。vue

閱讀本文,你將學到:java

1. 如何學習 JavaScript 基礎知識,會推薦不少學習資料
2. 如何學習調試 Vue 3 源碼
3. 如何學習源碼中優秀代碼和思想,投入到本身的項目中
4. Vue 3 源碼 shared 模塊中的幾十個實用工具函數
5. 個人一些經驗分享
複製代碼

shared模塊中57個工具函數,本次閱讀其中的30餘個node

2. 環境準備

2.1 讀開源項目 貢獻指南

打開 vue-next, 開源項目通常都能在 README.md 或者 .github/contributing.md 找到貢獻指南。ios

而貢獻指南寫了不少關於參與項目開發的信息。好比怎麼跑起來,項目目錄結構是怎樣的。怎麼投入開發,須要哪些知識儲備等。git

咱們能夠在 項目目錄結構 描述中,找到shared模塊。es6

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

README.mdcontributing.md 通常都是英文的。可能會難倒一部分人。其實看不懂,徹底能夠能夠藉助劃詞翻譯,整頁翻譯和百度翻譯等翻譯工具。再把英文加入後續學習計劃。

本文就是講shared模塊,對應的文件路徑是:vue-next/packages/shared/src/index.ts

也能夠用github1s訪問,速度更快。github1s packages/shared/src/index.ts

2.2 按照項目指南 打包構建代碼

爲了下降文章難度,我按照貢獻指南中方法打包把ts轉成了js。若是你須要打包,也能夠參考下文打包構建。

你須要確保 Node.js 版本是 10+, 並且 yarn 的版本是 1.x Yarn 1.x

你安裝的 Node.js 版本極可能是低於 10。最簡單的辦法就是去官網從新安裝。也可使用 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-analysis

npm install --global yarn
yarn # install the dependencies of the project
# yarn —ignore-scripts 忽略一些安裝,更快速
yarn build
複製代碼

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

固然,前面可能比較囉嗦。我能夠直接講 3. 工具函數。但經過我上文的介紹,即便是初學者,都能看懂一些開源項目源碼,也許就會有必定的成就感。 另外,面試問到被相似的問題或者筆試題時,你說看Vue3源碼學到的,面試官絕對對你另眼相看。

2.3 如何生成 sourcemap 調試 vue-next 源碼

熟悉個人讀者知道,我是常常強調生成sourcemap調試看源碼,因此順便提一下如何配置生成sourcemap,如何調試。這部分能夠簡單略過,動手操做時再仔細看。

其實貢獻指南裏描述了。

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執行,便可生成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官網找個例子,在 vue-next/examples/index.html。其內容引入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等進入函數,就能夠愉快的調試源碼了。

vue-next-debugger.png

3. 工具函數

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

咱們也能夠經過ts文件,查看使用函數的位置。同時在VSCode運行調試JS代碼,咱們比較推薦韓老師寫的code runner插件。

3.1 babelParserDefaultPlugins babel 解析默認插件

/** * 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'
];
複製代碼

這裏就是幾個默認插件。感興趣看英文註釋查看。

3.2 EMPTY_OBJ 空對象

const EMPTY_OBJ = (process.env.NODE_ENV !== 'production')
    ? Object.freeze({})
    : {};

// 例子:
// Object.freeze 是 凍結對象
// 凍結的對象最外層沒法修改。
const EMPTY_OBJ_1 = Object.freeze({});
EMPTY_OBJ_1.name = '若川';
console.log(EMPTY_OBJ_1.name); // undefined

const 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_ENVnode 項目中的一個環境變量,通常定義爲:developmentproduction。根據環境寫代碼。好比開發環境,有報錯等信息,生產環境則不須要這些報錯警告。

3.3 EMPTY_ARR 空數組

const EMPTY_ARR = (process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];

// 例子:
EMPTY_ARR.push(1) // 報錯,也就是爲啥生產環境仍是用 []
EMPTY_ARR.length = 3;
console.log(EMPTY_ARR.length); // 0
複製代碼

3.4 NOOP 空函數

const NOOP = () => { };

// 不少庫的源碼中都有這樣的定義函數,好比 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 的函數

/** * 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 是正則。^符號在開頭,則表示是什麼開頭。而在其餘地方是指非。

與之相反的是:$符合在結尾,則表示是以什麼結尾。

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

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

regex101

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

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

3.7 isModelListener 監聽器

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

const isModelListener = (key) => key.startsWith('onUpdate:');

// 例子:
isModelListener('onUpdate:change'); // true
isModelListener('1onUpdate:change'); // false
// startsWith 是 ES6 提供的方法
複製代碼

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

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

3.8 extend 繼承 合併

說合並可能更準確些。

const extend = Object.assign;

// 例子:
const data = { name: '若川' };
const data2 = entend(data, { mp: '若川視野', name: '是若川啊' });
console.log(data); // { name: "是若川啊", mp: "若川視野" }
console.log(data2); // { name: "是若川啊", mp: "若川視野" }
console.log(data === data2); // true
複製代碼

3.9 remove 移除數組的一項

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 實際上是一個很耗性能的方法。刪除數組中的一項,其餘元素都要移動位置。

引伸axios InterceptorManager 攔截器源碼 中,攔截器用數組存儲的。但實際移除攔截器時,只是把攔截器置爲 null 。而不是用splice移除。最後執行時爲 null 的不執行,一樣效果。axios 攔截器這個場景下,不得不說爲性能作到了很好的考慮。

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

// 代碼有刪減
// 聲明
this.handlers = [];

// 移除
if (this.handlers[id]) {
    this.handlers[id] = null;
}

// 執行
if (h !== null) {
    fn(h);
}
複製代碼

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

const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasOwn = (val, key) => hasOwnProperty.call(val, key);

// 例子:

// 特別提醒:__proto__ 是瀏覽器實現的原型寫法,後面還會用到
// 如今已經有提供好幾個原型相關的API
// Object.getPrototypeOf
// Object.setPrototypeOf
// Object.isPrototypeOf

// .call 則是函數裏 this 顯示指定覺得第一個參數,並執行函數。

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 判斷數組

const isArray = Array.isArray;

isArray([]); // true
const fakeArr = { __proto__: Array.prototype, length: 0 };
isArray(fakeArr); // false
fakeArr instanceof Array; // true
// 因此 instanceof 這種狀況 不許確
複製代碼

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 數據結構。它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。也就是說,Object 結構提供了「字符串—值」的對應,Map 結構提供了「值—值」的對應,是一種更完善的 Hash 結構實現。若是你須要「鍵值對」的數據結構,Map 比 Object 更合適。

3.13 isSet 判斷是否是 Set 對象

const isSet = (val) => toTypeString(val) === '[object Set]';

// 例子:
const set = new Set();
isSet(set); // true
複製代碼

ES6 提供了新的數據結構 Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。

Set自己是一個構造函數,用來生成 Set 數據結構。

3.14 isDate 判斷是否是 Date 對象

const isDate = (val) => val instanceof Date;

// 例子:
isDate(new Date()); // true

// `instanceof` 操做符左邊是右邊的實例。但不是很準,但通常夠用了。原理是根據原型鏈向上查找的。

isDate({__proto__ : new Date()); // true
// 其實是應該是 Object 纔對。
// 因此用 instanceof 判斷數組也不許確。
// 再好比
({__proto__: [] }) instanceof Array; // true
// 其實是對象。
// 因此用 數組自己提供的方法 Array.isArray 是比較準確的。
複製代碼

3.15 isFunction 判斷是否是函數

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

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 是函數,不須要用 new 調用。
複製代碼

ES6 引入了一種新的原始數據類型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 對於初學者來講可能比較難理解。可是重點內容,JS異步編程,要着重掌握。
// 如今 web 開發 Promise 和 async await 等很是經常使用。
複製代碼

能夠根據文末推薦的書籍看Promise相關章節掌握。同時也推薦這本迷你書JavaScript Promise迷你書(中文版)

3.20 objectToString 對象轉字符串

const objectToString = Object.prototype.toString;

// 對象轉字符串
複製代碼

3.21 toTypeString 對象轉字符串

const toTypeString = (value) => objectToString.call(value);

// call 是一個函數,第一個參數是 執行函數裏面 this 指向。
// 經過這個能得到 相似 "[object String]" 其中 String 是根據類型變化的
複製代碼

3.22 toRawType 對象轉字符串 截取後幾位

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

// 截取到
toRawType('');  'String'
複製代碼

能夠 截取到 String Array 等這些類型

JS 判斷數據類型很是重要的知識點。

JS 判斷類型也有 typeof ,但不是很準確,並且可以識別出的很少。

這些算是基礎知識

mdn typeof 文檔,文檔比較詳細,也實現了一個很完善的type函數,本文就不贅述了。

// 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 這個函數在不少源碼裏都有,好比 jQuery 源碼和 lodash 源碼等,具體實現不同
複製代碼

3.24 isIntegerKey 判斷是否是數字型的字符串key值

const isIntegerKey = (key) => isString(key) &&
    key !== 'NaN' &&
    key[0] !== '-' &&
    '' + parseInt(key, 10) === key;

// 例子:
isInegerKey('a'); // false
isInegerKey('0'); // true
isInegerKey('011'); // false
isInegerKey('11'); // true
// 其中 parseInt 第二個參數是進制。
// 字符串能用數組取值的形式取值。
// 還有一個 charAt 函數,但不經常使用 
'abc'.charAt(0) // 'a'
// charAt 與數組形式不一樣的是 取不到值會返回空字符串'',數組形式取值取不到則是 undefined
複製代碼

3.25 makeMap && isReservedProp

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

/** * 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));
    });
};
複製代碼

這個函數也是和上面 MakeMap 函數相似。只不過接收參數的是函數。 《JavaScript 設計模式與開發實踐》書中的第四章 JS單例模式也是相似的實現。

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

如下是一些正則,系統學習正則推薦老姚:《JavaScript 正則表達式迷你書》問世了!,看過的都說好。因此本文不會過多描述正則相關知識點。

// \w 是 0-9a-zA-Z_ 數字 大小寫字母和下劃線組成
// () 小括號是 分組捕獲
const camelizeRE = /-(\w)/g;
/** * @private */
// 首字母轉大寫
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 */
// 首字母轉大寫
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);
// 例子:
// 認爲 NaN 是不變的
hasChanged(NaN, NaN); // false
hasChanged(1, 1); // false
hasChanged(1, 2); // false
// 場景
// watch 監測值是否是變化了
複製代碼

3.28 invokeArrayFns 執行數組裏的函數

const invokeArrayFns = (fns, arg) => {
    for (let i = 0; i < fns.length; i++) {
        fns[i](arg);
    }
};

// 例子:
const arr = [
    function(val){
        console.log(val + '的博客地址是:https://lxchuan12.gitee.io');
    },
    function(val){
        console.log('百度搜索 若川 能夠找到' + val);
    },
    function(val){
        console.log('微信搜索 若川視野 能夠找到關注' + val);
    },
]
invokeArrayFns(arr, '我');
複製代碼

爲何這樣寫,咱們通常都是一個函數執行就行。

數組中存放函數,函數其實也算是數據。這種寫法方便統一執行多個函數。

3.29 def 定義對象屬性

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

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

Object.defineProperty 涉及到比較重要的知識點。
ES3中,除了一些內置屬性(如:Math.PI),對象的全部的屬性在任什麼時候候均可以被修改、插入、刪除。在ES5中,咱們能夠設置屬性是否能夠被改變或是被刪除——在這以前,它是內置屬性的特權。ES5中引入了屬性描述符的概念,咱們能夠經過它對所定義的屬性有更大的控制權。這些屬性描述符(特性)包括:

value——當試圖獲取屬性時所返回的值。
writable——該屬性是否可寫。
enumerable——該屬性在for in循環中是否會被枚舉。
configurable——該屬性是否可被刪除。
set()——該屬性的更新操做所調用的函數。
get()——獲取屬性值時所調用的函數。

另外,數據描述符(其中屬性爲:enumerableconfigurablevaluewritable)與存取描述符(其中屬性爲enumerableconfigurableset()get())之間是有互斥關係的。在定義了set()get()以後,描述符會認爲存取操做已被 定義了,其中再定義valuewritable引發錯誤

如下是ES3風格的屬性定義方式:

var person = {};
person.legs = 2;
複製代碼

如下是等價的ES5經過數據描述符定義屬性的方式:

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

其中, 除了value的默認值爲undefined之外,其餘的默認值都爲false。這就意味着,若是想要經過這一方式定義一個可寫的屬性,必須顯示將它們設爲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 轉數字

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 值,可是不許確的。

好比: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 指向。

初次執行確定是 _globalThisundefined。因此會執行後面的賦值語句。

若是存在 globalThis 就用 globalThisMDN globalThis

若是存在self,就用self。在 Web Worker 中不能訪問到 window 對象,可是咱們卻能經過 self 訪問到 Worker 環境中的全局對象。

若是存在window,就用window

若是存在global,就用globalNode環境下,使用global

若是都不存在,使用空對象。多是微信小程序環境下。

下次執行就直接返回 _globalThis,不須要第二次繼續判斷了。這種寫法值得咱們學習。

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

先推薦我認爲不錯的JavaScript API的幾篇文章和幾本值得讀的書。

JavaScript字符串全部API全解密

【深度長文】JavaScript數組全部API全解密

正則表達式前端使用手冊

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

JavaScript 對象全部API解析 lxchuan12.gitee.io/js-object-a…

MDN JavaScript

《JavaScript高級程序設計》第4版

《JavaScript 權威指南》第7版

《JavaScript面向對象編程2》 面向對象講的很詳細。

阮一峯老師:《ES6 入門教程》

《現代 JavaScript 教程》

《你不知道的JavaScript》上中卷

《JavaScript 設計模式與開發實踐》

我也是從小白看不懂書經歷過來的。到如今寫文章分享。

我看書的方法:多本書同時看,看相同相似的章節,好比函數。看完這本可能沒懂,看下一本,幾本書看下來基本就懂了,一遍沒看懂,再看幾遍,能夠避免遺忘,鞏固相關章節。固然,剛開始看書很難受,看不進。這些書大部分在微信讀書都有,若是習慣看紙質書,那能夠買來看。

這時能夠看些視頻和動手練習一些簡單的項目。

好比:能夠本身註冊一個github帳號,分章節小節,抄寫書中的代碼,提交到github,練習了纔會更有感受。

再好比 freeCodeCamp 中文在線學習網站 網站。看書是系統學習很是好的方法。後來我就是看源碼較多,寫文章分享出來給你們。

5. 總結

文中主要經過學習 shared 模塊下的幾十個工具函數,好比有:isPromisemakeMapcacheStringFunctioninvokeArrayFnsdefgetGlobalThis等等。

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

源碼也不是那麼可怕。日常咱們工做中也是常常能使用到這些工具函數。經過學習一些簡單源碼,拓展視野的同時,還能落實到本身工做開發中,收益相對比較高。


關於

做者:常以若川爲名混跡於江湖。前端路上 | PPT愛好者 | 所知甚少,惟善學。
公衆號若川視野
若川的博客
segmentfault若川視野專欄,開通了若川視野專欄,歡迎關注~
掘金專欄,歡迎關注~
知乎若川視野專欄,開通了若川視野專欄,歡迎關注~
github blog,求個star^_^~

相關文章
相關標籤/搜索