百忙之中(閒來無事)想抽點時間好好讀一下源碼,因而就選了Lodash來寫一個系列罷。寫了幾篇筆記之後發現不少函數大量依賴了內部的基礎函數,一次性讀完有點麻煩,因此仍是決定從基礎函數(沒有或者不多依賴其餘函數)看起。javascript
baseGetTag
,判斷變量類型。由於typeof在面對new Number(1)
這種狀況時力有不逮,因此lodash重寫了類型判斷。 javascript祕密花園中也提到了JavaScript 標準庫 推薦的是Object.prototype.toString.call(value)
這種方式來判斷類型,不過lodash明顯作的更多,前半段是推薦的方式,後半段目測是針對Symbol類型作了優化,具體步驟待研究,有了解的同窗歡迎告訴我。java
const objectProto = Object.prototype const hasOwnProperty = objectProto.hasOwnProperty const toString = objectProto.toString const symToStringTag = typeof Symbol != 'undefined' ? Symbol.toStringTag : undefined function baseGetTag(value) { if (value == null) { return value === undefined ? '[object Undefined]' : '[object Null]' } if (!(symToStringTag && symToStringTag in Object(value))) { return toString.call(value) } const isOwn = hasOwnProperty.call(value, symToStringTag) const tag = value[symToStringTag] let unmasked = false try { value[symToStringTag] = undefined unmasked = true } catch (e) {} const result = toString.call(value) if (unmasked) { if (isOwn) { value[symToStringTag] = tag } else { delete value[symToStringTag] } } return result } export default baseGetTag
getTag
是在baseGetTag
基礎上的包裝,主要是爲了兼容IE 11中的 data views, maps, sets, weak maps
和當Node.js < 6時的promises
,這一點在源碼註釋中已有體現git
import baseGetTag from './baseGetTag.js' /** `Object#toString` result references. */ const dataViewTag = '[object DataView]' const mapTag = '[object Map]' const objectTag = '[object Object]' const promiseTag = '[object Promise]' const setTag = '[object Set]' const weakMapTag = '[object WeakMap]' /** Used to detect maps, sets, and weakmaps. */ const dataViewCtorString = `${DataView}` const mapCtorString = `${Map}` const promiseCtorString = `${Promise}` const setCtorString = `${Set}` const weakMapCtorString = `${WeakMap}` let getTag = baseGetTag // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in // Node.js < 6. if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || (getTag(new Map) != mapTag) || (getTag(Promise.resolve()) != promiseTag) || (getTag(new Set) != setTag) || (getTag(new WeakMap) != weakMapTag)) { getTag = (value) => { const result = baseGetTag(value) const Ctor = result == objectTag ? value.constructor : undefined const ctorString = Ctor ? `${Ctor}` : '' if (ctorString) { switch (ctorString) { case dataViewCtorString: return dataViewTag case mapCtorString: return mapTag case promiseCtorString: return promiseTag case setCtorString: return setTag case weakMapCtorString: return weakMapTag } } return result } } export default getTag
isObjectLike
, 判斷是不是一個對象github
function isObjectLike(value) { return typeof value == 'object' && value !== null } export default isObjectLike
isArguments
調用了getTag
和isObjectLike
用來判斷參數是不是一個Arguments對象。數組
import getTag from './.internal/getTag.js' import isObjectLike from './isObjectLike' function isArguments(value) { return isObjectLike(value) && getTag(value) == '[object Arguments]' } export default isArguments
isFlattenable
調用isArguments
來判斷是否爲一個Arguments對象或數組或是一個能展開的Symbolpromise
import isArguments from '../isArguments.js' const spreadableSymbol = Symbol.isConcatSpreadable function isFlattenable(value) { return Array.isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]) } export default isFlattenable
baseFlatten
能夠展開數組的n層嵌套(扁平處理),依賴了isFlattenable
ide
// 例: baseFlatten([[[1,2,3],[2,2,3]],4,5,6], 2) // => [1, 2, 3, 2, 2, 3, 4, 5, 6] // isStrict == true baseFlatten([[1,2,3],[2,2,3],4,5,6], 1, null, true) // => [1, 2, 3, 2, 2, 3]
import isFlattenable from './isFlattenable.js' function baseFlatten(array, depth, predicate, isStrict, result) { predicate || (predicate = isFlattenable) // predicate參數兜底 result || (result = []) // 返回新數組 if (array == null) { return result } // 遞歸調用自己,嚴格模式`isStrict = true`直接退出循環 for (const value of array) { if (depth > 0 && predicate(value)) { if (depth > 1) { // Recursively flatten arrays (susceptible to call stack limits). baseFlatten(value, depth - 1, predicate, isStrict, result) } else { result.push(...value) } } else if (!isStrict) { result[result.length] = value } } return result } export default baseFlatten
isLength
,判斷是不是一個有效數字函數
const MAX_SAFE_INTEGER = 9007199254740991 // 最大數 function isLength(value) { // type是number而且大於等於0且小於等於最大數 return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER } export default isLength
isArrayLike
, 判斷是不是一個數組,依賴了isLength
優化
import isLength from './isLength.js' function isArrayLike(value) { // value不爲空,不是function且有length屬性 return value != null && typeof value != 'function' && isLength(value.length) } export default isArrayLike
isArrayLikeObject
,判斷是不是一個數組或者對象,依賴了isArrayLike
和isObjectLike
import isArrayLike from './isArrayLike.js' import isObjectLike from './isObjectLike.js' function isArrayLikeObject(value) { return isObjectLike(value) && isArrayLike(value) } export default isArrayLikeObject
eq
,判斷兩個值是否相等。
function eq(value, other) { return value === other || (value !== value && other !== other) } export default eq
||
前面好理解,判斷兩個值全等||
後面是爲了判斷 NaN
例:
const object = { 'a': 1 } const other = { 'a': 1 } eq(object, object) // => true eq(object, other) // => false eq('a', 'a') // => true eq('a', Object('a')) // => false eq(NaN, NaN) // => true
獲取鍵值對數組中的「鍵」的索引。例:
assocIndexOf([[1,2],[3,4],[5,6]], 2) // => -1 assocIndexOf([[1,2],[3,4],[5,6]], 3) // => 1
import eq from '../eq.js' function assocIndexOf(array, key) { let { length } = array while (length--) { if (eq(array[length][0], key)) { return length } } return -1 } export default assocIndexOf