参考链接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 遍历整个原型链上可被遍历的symbolvar 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,返回可以被遍历的symbolvar 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, 有长度, 不是functionfunction 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; };}());