参考链接https://github.com/yygmind/blog/issues/31
主方法
function cloneDeep(value) {
// 1 | 4 => 0001 | 0100 => 0101
return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
}
function baseClone(value, bitmask, customizer, key, object, stack) {
var result,
// 位运算
isDeep = bitmask & CLONE_DEEP_FLAG, // true: 0101 & 0001 => 0001 => true
isFlat = bitmask & CLONE_FLAT_FLAG, // false: 0101 & 0010 => 0000 => false
isFull = bitmask & CLONE_SYMBOLS_FLAG; // true: 0101 & 0100 => 0100 => true
// deepClone没有customizer
if (customizer) {
result = object ? customizer(value, key, object, stack) : customizer(value);
}
if (result !== undefined) {
return result;
}
// isObject => typeof value 不为 null 且 为 object 或 function
// type = typeof value; type !== null && (type == 'object' || type == 'function')
if (!isObject(value)) {
return value;
}
// isArray => Array.isArray
var isArr = isArray(value);
if (isArr) {
// new value.constructor(value.length), 得到一个长度和value一样的数组
// value.constructor === Array
// 如果是RegExp.exec得到的array, 则把index 和 input 写回到 这个空的数组中
// result = new value.constructor(value.length)
// if(length && typeof value[0] == 'string'
// && Object.prototype.hasOwnProperty.call(value, 'index'))
// {result.input = value.input; result.index = value.index}
result = initCloneArray(value);
if (!isDeep) {
// 一个一个的把value中的值赋到result中
// 使得value[key] === result[key] => true;
// while(++i < value.length) result[i] = value[i];
return copyArray(value, result);
}
} else {
// 标题 getTag
var tag = getTag(value),
// [object Function] || [object GeneratorFunction]
isFunc = tag == funcTag || tag == genTag;
// isBuffer: 判断是否有exports 是否有module
// module.exports 是否等于 exports
// 是否有Buffer 是否有isBuffer
// 满足以上返回isBuffer
// 否者返回一个stubFalse = () => false
if (isBuffer(value)) {
// 调用buffer本身的方法进行复制
return cloneBuffer(value, isDeep);
}
// [object Object] || [object Arguments] || (isFunc && !object) !object => true
if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
// (false || isFunc) ? {} : 标题 initCloneObject
result = (isFlat || isFunc) ? {} : initCloneObject(value);
// 是Deep 但可以看看简单的clone是怎么处理的(待填的坑位1)
if (!isDeep) {
return isFlat
? copySymbolsIn(value, baseAssignIn(result, value))
: copySymbols(value, baseAssign(result, value));
}
} else {
// 如果是 [object Error] [object Function] [object WeakMap]
if (!cloneableTags[tag]) {
return object ? value : {};
}
// 标题 initCloneByTag(value, tag, isDeep);
result = initCloneByTag(value, tag, isDeep);
}
}
// 获取stack,如果已经存在对应的value,直接返回这个值
// 如果没有则把值记录下来备用
// Check for circular references and return its corresponding clone.
stack || (stack = new Stack);
var stacked = stack.get(value);
if (stacked) {
return stacked;
}
stack.set(value, result);
// Set和Map的设置需要特殊处理
if (isSet(value)) {
value.forEach(function(subValue) {
result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
});
} else if (isMap(value)) {
value.forEach(function(subValue, key) {
result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
});
}
// 根据isFull 和 isFlat 判断对应的keysFunc
// isFull = true isFlat => false
// keysFunc = getAllKeys
var keysFunc = isFull
? (
isFlat
? getAllKeysIn // 自身和prototype上的属性包括symbol 标题 getAllKeysIn
: getAllKeys // 自身的属性包括symbol 标题 getAllKeys
)
: (
isFlat
? keysIn // 自身和prototype上的属性不包括symbol 标题 keysIn
: keys // 自身和prototype上的属性不包括symbol 标题 keys
);
// 获取value的keys
var props = isArr ? undefined : keysFunc(value);
arrayEach(props || value, function(subValue, key) {
// 其中初始时,key是下标,所以可以同时处理objectKeys 和 array
// 当为objectkeys时,转换key为实际的key, subValue为实际的subValue
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;
}
initCloneArray
function initCloneArray(array) {
var length = array.length,
result = new array.constructor(length);
// Add properties assigned by `RegExp#exec`.
if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
result.index = array.index;
result.input = array.input;
}
return result;
}
getTag
var getTag = baseGetTag;
function baseGetTag(value) {
// undefined == null
if (value == null) {
// 返回 [object Undefined] [object Null]
return value === undefined ? undefinedTag : nullTag;
}
// symToStringTag = Symbol ? Symbol.toStringTag : undefined;
return (symToStringTag && symToStringTag in Object(value))
// 处理 Symbol.toStringTag
? getRawTag(value)
// object.prototype.toString.call(value)
: objectToString(value);
}
// 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) ||
(Map && getTag(new Map) != mapTag) ||
(Promise && getTag(Promise.resolve()) != promiseTag) ||
(Set && getTag(new Set) != setTag) ||
(WeakMap && getTag(new WeakMap) != weakMapTag)) {
getTag = function(value) {
var result = baseGetTag(value),
Ctor = result == objectTag ? value.constructor : undefined,
ctorString = Ctor ? toSource(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;
};
}
initCloneObject
function initCloneObject(object) {
// 构造函数是 function 且 不是property value 则调用 baseCreate(getProperty(object))
return (typeof object.constructor == 'function' && !isPrototype(object))
? baseCreate(getPrototype(object))
: {};
}
// getPrototype = function(arg){ return Object.getPrototypeOf(Object(arg))}
var getPrototype = overArg(Object.getPrototypeOf, Object);
function overArg(func, transform) {
return function(arg) {
return func(transform(arg));
};
}
// var baseCreate = function(proto){
// if(!isObject(proto){ return {}}
//
// if(Object.create){return Object.create(proto)}
//
// // 如果没有Object.create ie8及一下没有Object.create
// object.prototype = proto;
// var result = new object;
// object.prototype = undefined;
// return result;
// }
var baseCreate = (function() {
function object() {}
return function(proto) {
if (!isObject(proto)) {
return {};
}
if (objectCreate) {
return objectCreate(proto);
}
object.prototype = proto;
var result = new object;
object.prototype = undefined;
return result;
};
}());
initCloneByTag
function initCloneByTag(object, tag, isDeep) {
// 获取object的构造函数
var 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:
return cloneRegExp(object);
case setTag:
return new Ctor;
case symbolTag:
return cloneSymbol(object);
}
}
getAllKeysIn
function getAllKeysIn(object) {
// keysIn: 标题 KesIn
// getSymbolsIn: 下方
return baseGetAllKeys(object, keysIn, getSymbolsIn);
}
// 调用keysIn把对应的keys放到result中,若非数组,还会调用symbolsFunc获取symbols并到result中
function baseGetAllKeys(object, keysFunc, symbolsFunc) {
var result = keysFunc(object);
return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
}
// 没有symbol返回() => []
// 有symbol 返回function 遍历整个原型链上可被遍历的symbol
var getSymbolsIn = !nativeGetSymbols ? stubArray : function (object) {
var result = [];
while (object) {
// 把可以遍历的symbol赋值到result上
arrayPush(result, getSymbols(object));
// object = object.prototype // 相当于遍历整个原型链上可被遍历的symbol
object = getPrototype(object);
}
return result;
};
// 获取object的symbols,返回可以被遍历的symbol
var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
if (object == null) {
return [];
}
object = Object(object);
return arrayFilter(nativeGetSymbols(object), function(symbol) {
return propertyIsEnumerable.call(object, symbol);
});
};
getAllKeys
function getAllKeys(object) {
// keys: 下方
return baseGetAllKeys(object, keys, getSymbols);
keysIn
function keysIn(object) {
return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);
}
// 判断是否为类数组: 不为null, 有长度, 不是function
function isArrayLike(value) {
return value != null && isLength(value.length) && !isFunction(value);
}
//
function arrayLikeKeys(value, inherited) {
var isArr = isArray(value),
isArg = !isArr && isArguments(value),
isBuff = !isArr && !isArg && isBuffer(value),
isType = !isArr && !isArg && !isBuff && isTypedArray(value),
skipIndexes = isArr || isArg || isBuff || isType,
result = skipIndexes ? baseTimes(value.length, String) : [],
length = result.length;
for (var key in value) {
if ((inherited || hasOwnProperty.call(value, key)) &&
!(skipIndexes && (
// Safari 9 has enumerable `arguments.length` in strict mode.
key == 'length' ||
// Node.js 0.10 has enumerable non-index properties on buffers.
(isBuff && (key == 'offset' || key == 'parent')) ||
// PhantomJS 2 has enumerable non-index properties on typed arrays.
(isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
// Skip index properties.
isIndex(key, length)
))) {
result.push(key);
}
}
return result;
}
//
function baseKeysIn(object) {
// 不是object => nativeKeysIn(object) => 转成object,遍历key并返回
if (!isObject(object)) {
return nativeKeysIn(object);
}
var isProto = isPrototype(object),
result = [];
// 遍历所有的key
for (var key in object) {
// push 不等于 constructor 的key 或者 等于 constructor 但不是型对象 或者 自身原型链上没有 constructor 的
if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
result.push(key);
}
}
return result;
}
keys
function keys(object) {
return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}
function baseKeys(object) {
if (!isPrototype(object)) {
// 简单的返回 Object.keys();
return nativeKeys(object);
}
var result = [];
for (var key in Object(object)) {
if (hasOwnProperty.call(object, key) && key != 'constructor') {
result.push(key);
}
}
return result;
}
待填的坑位
1. 当不是deepClone时,如何复制[object Object] [object Arguments] [object Function] [object GeneratorFunction]
笔记
Object.create的实现
var baseCreate = (function() {
function object() {}
return function(proto) {
object.prototype = proto;
var result = new object;
object.prototype = undefined;
return result;
};
}());