参考链接https://github.com/yygmind/blog/issues/31

主方法

  1. function cloneDeep(value) {
  2. // 1 | 4 => 0001 | 0100 => 0101
  3. return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
  4. }
  5. function baseClone(value, bitmask, customizer, key, object, stack) {
  6. var result,
  7. // 位运算
  8. isDeep = bitmask & CLONE_DEEP_FLAG, // true: 0101 & 0001 => 0001 => true
  9. isFlat = bitmask & CLONE_FLAT_FLAG, // false: 0101 & 0010 => 0000 => false
  10. isFull = bitmask & CLONE_SYMBOLS_FLAG; // true: 0101 & 0100 => 0100 => true
  11. // deepClone没有customizer
  12. if (customizer) {
  13. result = object ? customizer(value, key, object, stack) : customizer(value);
  14. }
  15. if (result !== undefined) {
  16. return result;
  17. }
  18. // isObject => typeof value 不为 null 且 为 object 或 function
  19. // type = typeof value; type !== null && (type == 'object' || type == 'function')
  20. if (!isObject(value)) {
  21. return value;
  22. }
  23. // isArray => Array.isArray
  24. var isArr = isArray(value);
  25. if (isArr) {
  26. // new value.constructor(value.length), 得到一个长度和value一样的数组
  27. // value.constructor === Array
  28. // 如果是RegExp.exec得到的array, 则把index 和 input 写回到 这个空的数组中
  29. // result = new value.constructor(value.length)
  30. // if(length && typeof value[0] == 'string'
  31. // && Object.prototype.hasOwnProperty.call(value, 'index'))
  32. // {result.input = value.input; result.index = value.index}
  33. result = initCloneArray(value);
  34. if (!isDeep) {
  35. // 一个一个的把value中的值赋到result中
  36. // 使得value[key] === result[key] => true;
  37. // while(++i < value.length) result[i] = value[i];
  38. return copyArray(value, result);
  39. }
  40. } else {
  41. // 标题 getTag
  42. var tag = getTag(value),
  43. // [object Function] || [object GeneratorFunction]
  44. isFunc = tag == funcTag || tag == genTag;
  45. // isBuffer: 判断是否有exports 是否有module
  46. // module.exports 是否等于 exports
  47. // 是否有Buffer 是否有isBuffer
  48. // 满足以上返回isBuffer
  49. // 否者返回一个stubFalse = () => false
  50. if (isBuffer(value)) {
  51. // 调用buffer本身的方法进行复制
  52. return cloneBuffer(value, isDeep);
  53. }
  54. // [object Object] || [object Arguments] || (isFunc && !object) !object => true
  55. if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
  56. // (false || isFunc) ? {} : 标题 initCloneObject
  57. result = (isFlat || isFunc) ? {} : initCloneObject(value);
  58. // 是Deep 但可以看看简单的clone是怎么处理的(待填的坑位1)
  59. if (!isDeep) {
  60. return isFlat
  61. ? copySymbolsIn(value, baseAssignIn(result, value))
  62. : copySymbols(value, baseAssign(result, value));
  63. }
  64. } else {
  65. // 如果是 [object Error] [object Function] [object WeakMap]
  66. if (!cloneableTags[tag]) {
  67. return object ? value : {};
  68. }
  69. // 标题 initCloneByTag(value, tag, isDeep);
  70. result = initCloneByTag(value, tag, isDeep);
  71. }
  72. }
  73. // 获取stack,如果已经存在对应的value,直接返回这个值
  74. // 如果没有则把值记录下来备用
  75. // Check for circular references and return its corresponding clone.
  76. stack || (stack = new Stack);
  77. var stacked = stack.get(value);
  78. if (stacked) {
  79. return stacked;
  80. }
  81. stack.set(value, result);
  82. // Set和Map的设置需要特殊处理
  83. if (isSet(value)) {
  84. value.forEach(function(subValue) {
  85. result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
  86. });
  87. } else if (isMap(value)) {
  88. value.forEach(function(subValue, key) {
  89. result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
  90. });
  91. }
  92. // 根据isFull 和 isFlat 判断对应的keysFunc
  93. // isFull = true isFlat => false
  94. // keysFunc = getAllKeys
  95. var keysFunc = isFull
  96. ? (
  97. isFlat
  98. ? getAllKeysIn // 自身和prototype上的属性包括symbol 标题 getAllKeysIn
  99. : getAllKeys // 自身的属性包括symbol 标题 getAllKeys
  100. )
  101. : (
  102. isFlat
  103. ? keysIn // 自身和prototype上的属性不包括symbol 标题 keysIn
  104. : keys // 自身和prototype上的属性不包括symbol 标题 keys
  105. );
  106. // 获取value的keys
  107. var props = isArr ? undefined : keysFunc(value);
  108. arrayEach(props || value, function(subValue, key) {
  109. // 其中初始时,key是下标,所以可以同时处理objectKeys 和 array
  110. // 当为objectkeys时,转换key为实际的key, subValue为实际的subValue
  111. if (props) {
  112. key = subValue;
  113. subValue = value[key];
  114. }
  115. // 合并值
  116. // Recursively populate clone (susceptible to call stack limits).
  117. assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
  118. });
  119. return result;
  120. }

initCloneArray

  1. function initCloneArray(array) {
  2. var length = array.length,
  3. result = new array.constructor(length);
  4. // Add properties assigned by `RegExp#exec`.
  5. if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
  6. result.index = array.index;
  7. result.input = array.input;
  8. }
  9. return result;
  10. }

getTag

  1. var getTag = baseGetTag;
  2. function baseGetTag(value) {
  3. // undefined == null
  4. if (value == null) {
  5. // 返回 [object Undefined] [object Null]
  6. return value === undefined ? undefinedTag : nullTag;
  7. }
  8. // symToStringTag = Symbol ? Symbol.toStringTag : undefined;
  9. return (symToStringTag && symToStringTag in Object(value))
  10. // 处理 Symbol.toStringTag
  11. ? getRawTag(value)
  12. // object.prototype.toString.call(value)
  13. : objectToString(value);
  14. }
  15. // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
  16. // 处理兼容
  17. if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
  18. (Map && getTag(new Map) != mapTag) ||
  19. (Promise && getTag(Promise.resolve()) != promiseTag) ||
  20. (Set && getTag(new Set) != setTag) ||
  21. (WeakMap && getTag(new WeakMap) != weakMapTag)) {
  22. getTag = function(value) {
  23. var result = baseGetTag(value),
  24. Ctor = result == objectTag ? value.constructor : undefined,
  25. ctorString = Ctor ? toSource(Ctor) : '';
  26. if (ctorString) {
  27. switch (ctorString) {
  28. case dataViewCtorString: return dataViewTag;
  29. case mapCtorString: return mapTag;
  30. case promiseCtorString: return promiseTag;
  31. case setCtorString: return setTag;
  32. case weakMapCtorString: return weakMapTag;
  33. }
  34. }
  35. return result;
  36. };
  37. }

initCloneObject

  1. function initCloneObject(object) {
  2. // 构造函数是 function 且 不是property value 则调用 baseCreate(getProperty(object))
  3. return (typeof object.constructor == 'function' && !isPrototype(object))
  4. ? baseCreate(getPrototype(object))
  5. : {};
  6. }
  7. // getPrototype = function(arg){ return Object.getPrototypeOf(Object(arg))}
  8. var getPrototype = overArg(Object.getPrototypeOf, Object);
  9. function overArg(func, transform) {
  10. return function(arg) {
  11. return func(transform(arg));
  12. };
  13. }
  14. // var baseCreate = function(proto){
  15. // if(!isObject(proto){ return {}}
  16. //
  17. // if(Object.create){return Object.create(proto)}
  18. //
  19. // // 如果没有Object.create ie8及一下没有Object.create
  20. // object.prototype = proto;
  21. // var result = new object;
  22. // object.prototype = undefined;
  23. // return result;
  24. // }
  25. var baseCreate = (function() {
  26. function object() {}
  27. return function(proto) {
  28. if (!isObject(proto)) {
  29. return {};
  30. }
  31. if (objectCreate) {
  32. return objectCreate(proto);
  33. }
  34. object.prototype = proto;
  35. var result = new object;
  36. object.prototype = undefined;
  37. return result;
  38. };
  39. }());

initCloneByTag

  1. function initCloneByTag(object, tag, isDeep) {
  2. // 获取object的构造函数
  3. var Ctor = object.constructor;
  4. // 根据类型处理对应的值
  5. switch (tag) {
  6. case arrayBufferTag:
  7. return cloneArrayBuffer(object);
  8. case boolTag:
  9. case dateTag:
  10. return new Ctor(+object);
  11. case dataViewTag:
  12. return cloneDataView(object, isDeep);
  13. case float32Tag: case float64Tag:
  14. case int8Tag: case int16Tag: case int32Tag:
  15. case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
  16. return cloneTypedArray(object, isDeep);
  17. case mapTag:
  18. return new Ctor;
  19. case numberTag:
  20. case stringTag:
  21. return new Ctor(object);
  22. case regexpTag:
  23. return cloneRegExp(object);
  24. case setTag:
  25. return new Ctor;
  26. case symbolTag:
  27. return cloneSymbol(object);
  28. }
  29. }

getAllKeysIn

  1. function getAllKeysIn(object) {
  2. // keysIn: 标题 KesIn
  3. // getSymbolsIn: 下方
  4. return baseGetAllKeys(object, keysIn, getSymbolsIn);
  5. }
  6. // 调用keysIn把对应的keys放到result中,若非数组,还会调用symbolsFunc获取symbols并到result中
  7. function baseGetAllKeys(object, keysFunc, symbolsFunc) {
  8. var result = keysFunc(object);
  9. return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
  10. }
  11. // 没有symbol返回() => []
  12. // 有symbol 返回function 遍历整个原型链上可被遍历的symbol
  13. var getSymbolsIn = !nativeGetSymbols ? stubArray : function (object) {
  14. var result = [];
  15. while (object) {
  16. // 把可以遍历的symbol赋值到result上
  17. arrayPush(result, getSymbols(object));
  18. // object = object.prototype // 相当于遍历整个原型链上可被遍历的symbol
  19. object = getPrototype(object);
  20. }
  21. return result;
  22. };
  23. // 获取object的symbols,返回可以被遍历的symbol
  24. var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
  25. if (object == null) {
  26. return [];
  27. }
  28. object = Object(object);
  29. return arrayFilter(nativeGetSymbols(object), function(symbol) {
  30. return propertyIsEnumerable.call(object, symbol);
  31. });
  32. };

getAllKeys

  1. function getAllKeys(object) {
  2. // keys: 下方
  3. return baseGetAllKeys(object, keys, getSymbols);

keysIn

  1. function keysIn(object) {
  2. return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);
  3. }
  4. // 判断是否为类数组: 不为null, 有长度, 不是function
  5. function isArrayLike(value) {
  6. return value != null && isLength(value.length) && !isFunction(value);
  7. }
  8. //
  9. function arrayLikeKeys(value, inherited) {
  10. var isArr = isArray(value),
  11. isArg = !isArr && isArguments(value),
  12. isBuff = !isArr && !isArg && isBuffer(value),
  13. isType = !isArr && !isArg && !isBuff && isTypedArray(value),
  14. skipIndexes = isArr || isArg || isBuff || isType,
  15. result = skipIndexes ? baseTimes(value.length, String) : [],
  16. length = result.length;
  17. for (var key in value) {
  18. if ((inherited || hasOwnProperty.call(value, key)) &&
  19. !(skipIndexes && (
  20. // Safari 9 has enumerable `arguments.length` in strict mode.
  21. key == 'length' ||
  22. // Node.js 0.10 has enumerable non-index properties on buffers.
  23. (isBuff && (key == 'offset' || key == 'parent')) ||
  24. // PhantomJS 2 has enumerable non-index properties on typed arrays.
  25. (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
  26. // Skip index properties.
  27. isIndex(key, length)
  28. ))) {
  29. result.push(key);
  30. }
  31. }
  32. return result;
  33. }
  34. //
  35. function baseKeysIn(object) {
  36. // 不是object => nativeKeysIn(object) => 转成object,遍历key并返回
  37. if (!isObject(object)) {
  38. return nativeKeysIn(object);
  39. }
  40. var isProto = isPrototype(object),
  41. result = [];
  42. // 遍历所有的key
  43. for (var key in object) {
  44. // push 不等于 constructor 的key 或者 等于 constructor 但不是型对象 或者 自身原型链上没有 constructor 的
  45. if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
  46. result.push(key);
  47. }
  48. }
  49. return result;
  50. }

keys

  1. function keys(object) {
  2. return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
  3. }
  4. function baseKeys(object) {
  5. if (!isPrototype(object)) {
  6. // 简单的返回 Object.keys();
  7. return nativeKeys(object);
  8. }
  9. var result = [];
  10. for (var key in Object(object)) {
  11. if (hasOwnProperty.call(object, key) && key != 'constructor') {
  12. result.push(key);
  13. }
  14. }
  15. return result;
  16. }

待填的坑位

1. 当不是deepClone时,如何复制[object Object] [object Arguments] [object Function] [object GeneratorFunction]

笔记

Object.create的实现

  1. var baseCreate = (function() {
  2. function object() {}
  3. return function(proto) {
  4. object.prototype = proto;
  5. var result = new object;
  6. object.prototype = undefined;
  7. return result;
  8. };
  9. }());