lodash 源码阅读-baseClone
源码
/**
* The base implementation of `clone` and `cloneDeep` which tracks
* traversed objects.
*
* @private
* @param {*} value The value to clone. <-- 要克隆的值
* @param {number} bitmask The bitmask flags.
* 1 - Deep clone
* 2 - Flatten inherited properties
* 4 - Clone symbols
* @param {Function} [customizer] The function to customize cloning. <-- 自定义克隆的方法
* @param {string} [key] The key of `value`. <-- value 的 key
* @param {Object} [object] The parent object of `value`. <-- value 的父级对象
* @param {Object} [stack] Tracks traversed objects and their clone counterparts. <-- 追踪遍历的对象以及克隆的副本
* @returns {*} Returns the cloned value.
*/
function baseClone(value, bitmask, customizer, key, object, stack) {
let result
const isDeep = bitmask & CLONE_DEEP_FLAG // & 是 位运算符 见下面补充
const isFlat = bitmask & CLONE_FLAT_FLAG
const isFull = bitmask & CLONE_SYMBOLS_FLAG
// 如果有自定义的方法,调用自定义方法
if (customizer) {
result = object ? customizer(value, key, object, stack) : customizer(value)
}
if (result !== undefined) {
return result
}
// 判断是不是对象
if (!isObject(value)) {
return value
}
// 判断是不是数组
const isArr = Array.isArray(value)
// 返回对象的 toString() 后的值,比如 '[object Object]'
const tag = getTag(value)
// 如果是数组
if (isArr) {
result = initCloneArray(value) // 初始化一个和 value 长度一样,但是值都是 empty 的数组
if (!isDeep) {
return copyArray(value, result) // 把 value 的 值 拷贝到 result 上
}
}
// 如果不是数组
else {
const isFunc = typeof value == 'function' // 是否是 function
// Buffer 参考 NodeJS
if (isBuffer(value)) {
return cloneBuffer(value, isDeep)
}
// 如果是 对象 || arguments || function 且没有父级。tag 类型参考下面
if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
// initCloneObject 拷贝 value 的原型对象
result = (isFlat || isFunc) ? {} : initCloneObject(value)
if (!isDeep) {
return isFlat
// keysIn 返回 value 自身的以及继承的可枚举属性
// copyObject <-- 将 value 中包含 keysIn 返回的属性拷贝到 result 上
// copySymbolsIn 将 value 的自身和继承的 symbol 属性拷贝给 result
? copySymbolsIn(value, copyObject(value, keysIn(value), result))
// Object.assign(result, value) 将 value 的可枚举属性拷贝给 result
// copySymbols 将 value 的 symbol 属性拷贝给 result
: copySymbols(value, Object.assign(result, value))
}
}
else {
// cloneableTags 参考下面
if (isFunc || !cloneableTags[tag]) {
return object ? value : {}
}
// initCloneByTag 见下面
result = initCloneByTag(value, tag, isDeep)
}
}
// Check for circular references and return its corresponding clone.
stack || (stack = new Stack)
const stacked = stack.get(value)
if (stacked) {
return stacked
}
stack.set(value, result)
if (tag == mapTag) {
value.forEach((subValue, key) => {
result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack))
})
return result
}
if (tag == setTag) {
value.forEach((subValue) => {
result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack))
})
return result
}
if (isTypedArray(value)) {
return result
}
const keysFunc = isFull
? (isFlat ? getAllKeysIn : getAllKeys)
: (isFlat ? keysIn : keys)
const props = isArr ? undefined : keysFunc(value)
arrayEach(props || value, (subValue, key) => {
if (props) {
key = subValue
subValue = value[key]
}
// Recursively populate clone (susceptible to call stack limits).
assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack))
})
return result
}
& 位运算符
a & b;
// 在a,b的位表示中,每一个对应的位都为1则返回1, 否则返回0.
isObject
判断是否是对象。
getTag
获取 toString() 之后的 tag。
initCloneArray
/** Used to check objects for own properties. */
const hasOwnProperty = Object.prototype.hasOwnProperty
/**
* Initializes an array clone.
*
* @private
* @param {Array} array The array to clone.
* @returns {Array} Returns the initialized clone.
*/
function initCloneArray(array) {
const { length } = array
const result = new array.constructor(length) // 调用 array 的构造函数,创建一个新数组
// Add properties assigned by `RegExp#exec`. 增加正则 exec 后返回的数组的属性
/**
* 比如
* /a/.exec('abc');
* ["a", index: 0, input: "abc", groups: undefined, length: 1]
*/
if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
result.index = array.index
result.input = array.input
}
return result
}
Object.prototype.hasOwnProperty
返回一个布尔值,表明对象自身属性中是否具有指定的值。
const hasOwnProperty = Object.prototype.hasOwnProperty;
const arr = ['a', 'b'];
hasOwnProperty.call(arr, 'index'); // false
copyArray
tag 类型
/** `Object#toString` result references. */
const argsTag = '[object Arguments]'
const arrayTag = '[object Array]'
const boolTag = '[object Boolean]'
const dateTag = '[object Date]'
const errorTag = '[object Error]'
const mapTag = '[object Map]'
const numberTag = '[object Number]'
const objectTag = '[object Object]'
const regexpTag = '[object RegExp]'
const setTag = '[object Set]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
const weakMapTag = '[object WeakMap]'
const arrayBufferTag = '[object ArrayBuffer]'
const dataViewTag = '[object DataView]'
const float32Tag = '[object Float32Array]'
const float64Tag = '[object Float64Array]'
const int8Tag = '[object Int8Array]'
const int16Tag = '[object Int16Array]'
const int32Tag = '[object Int32Array]'
const uint8Tag = '[object Uint8Array]'
const uint8ClampedTag = '[object Uint8ClampedArray]'
const uint16Tag = '[object Uint16Array]'
const uint32Tag = '[object Uint32Array]'
initCloneObject
copySymbols
cloneableTags
/** Used to identify `toStringTag` values supported by `clone`. */
const cloneableTags = {}
cloneableTags[argsTag] = cloneableTags[arrayTag] =
cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
cloneableTags[boolTag] = cloneableTags[dateTag] =
cloneableTags[float32Tag] = cloneableTags[float64Tag] =
cloneableTags[int8Tag] = cloneableTags[int16Tag] =
cloneableTags[int32Tag] = cloneableTags[mapTag] =
cloneableTags[numberTag] = cloneableTags[objectTag] =
cloneableTags[regexpTag] = cloneableTags[setTag] =
cloneableTags[stringTag] = cloneableTags[symbolTag] =
cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true
cloneableTags[errorTag] = cloneableTags[weakMapTag] = false
initCloneByTag
/**
* Initializes an object clone based on its `toStringTag`.
*
* **Note:** This function only supports cloning values with tags of
* `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
*
* @private
* @param {Object} object The object to clone.
* @param {string} tag The `toStringTag` of the object to clone.
* @param {boolean} [isDeep] Specify a deep clone.
* @returns {Object} Returns the initialized clone.
*/
function initCloneByTag(object, tag, isDeep) {
const Ctor = object.constructor
switch (tag) {
case arrayBufferTag:
return cloneArrayBuffer(object)
case boolTag:
case dateTag:
return new Ctor(+object)
case dataViewTag:
return cloneDataView(object, isDeep)
case float32Tag: case float64Tag:
case int8Tag: case int16Tag: case int32Tag:
case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
return cloneTypedArray(object, isDeep)
case mapTag:
return new Ctor
case numberTag:
case stringTag:
return new Ctor(object)
case regexpTag:
// 调用 regexp.constructor 方法克隆
return cloneRegExp(object)
case setTag:
return new Ctor
case symbolTag:
// 调用 Symbol.protoype.valueOf 方法克隆
return cloneSymbol(object)
}
}