zl程序教程

您现在的位置是:首页 >  其它

当前栏目

_.cloneDeepWith(value, [customizer])

value _.
2023-09-11 14:15:01 时间

120

_.cloneDeepWith(value, [customizer])
_.cloneDeepWith此方法与cloneWith类似,区别是会递归深度克隆value对象
自定义传入的方法调用时会传入四个参数:value,key,object,stack

参数

value (*): 需要递归深度克隆的值
[customizer] (Function): 自定义克隆函数

返回值

(*): 返回深度克隆好的值

例子

 

function customizer(value) {
  if (_.isElement(value)) {
    return value.cloneNode(true);
  }
}
 
var el = _.cloneDeepWith(document.body, customizer);
 
console.log(el === document.body);
// => false
console.log(el.nodeName);
// => 'BODY'
console.log(el.childNodes.length);
// => 20

 

源代码:

import baseClone from './.internal/baseClone.js'

/** Used to compose bitmasks for cloning. */
const CLONE_DEEP_FLAG = 1
const CLONE_SYMBOLS_FLAG = 4

/**
 * This method is like `cloneWith` except that it recursively clones `value`.
 * The customizer is invoked with up to four arguments
 * (value [, index|key, object, stack]).
 *
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to recursively clone.
 * @param {Function} [customizer] The function to customize cloning.
 * @returns {*} Returns the deep cloned value.
 * @see cloneWith
 * @example
 *
 * function customizer(value) {
 *   if (isElement(value)) {
 *     return value.cloneNode(true)
 *   }
 * }
 *
 * const el = cloneDeepWith(document.body, customizer)
 *
 * console.log(el === document.body)
 * // => false
 * console.log(el.nodeName)
 * // => 'BODY'
 * console.log(el.childNodes.length)
 * // => 20
 */
//此方法与cloneWith类似,区别是会递归深度克隆value对象
//自定义传入的方法调用时会传入四个参数:value,key,object,stack
function cloneDeepWith(value, customizer) {
  customizer = typeof customizer == 'function' ? customizer : undefined
  //自定义克隆方法判断类型
  return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer)
}

export default cloneDeepWith

baseClone

import Stack from './Stack.js'
import arrayEach from './arrayEach.js'
import assignValue from './assignValue.js'
import baseAssign from './baseAssign.js'
import baseAssignIn from './baseAssignIn.js'
import cloneBuffer from './cloneBuffer.js'
import copyArray from './copyArray.js'
import cloneArrayBuffer from './cloneArrayBuffer.js'
import cloneDataView from './cloneDataView.js'
import cloneRegExp from './cloneRegExp.js'
import cloneSymbol from './cloneSymbol.js'
import cloneTypedArray from './cloneTypedArray.js'
import copySymbols from './copySymbols.js'
import copySymbolsIn from './copySymbolsIn.js'
import getAllKeys from './getAllKeys.js'
import getAllKeysIn from './getAllKeysIn.js'
import getTag from './getTag.js'
import initCloneObject from './initCloneObject.js'
import isBuffer from '../isBuffer.js'
import isObject from '../isObject.js'
import keys from '../keys.js'
import keysIn from '../keysIn.js'

/** Used to compose bitmasks for cloning. */
//判断克隆的类型,使用二进制掩码标识了深克隆,浅克隆,展平克隆(将继承属性展平)
const CLONE_DEEP_FLAG = 1
const CLONE_FLAT_FLAG = 2
const CLONE_SYMBOLS_FLAG = 4

/** `Object#toString` result references. */
//对象的toStringTag
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]'

/** Used to identify `toStringTag` values supported by `clone`. */
//用来确定指定的toStringTag是否支持克隆
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

/** Used to check objects for own properties. */
//用于检测是否是对象自身的属性
const hasOwnProperty = Object.prototype.hasOwnProperty

/**
 * 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.
 */
//基于toStringTag初始化对象克隆
//注意:此方法只支持如下类型:`Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
function initCloneByTag(object, tag, isDeep) {
  const Ctor = object.constructor//object的构造函数
  switch (tag) {
    case arrayBufferTag://ArrayBuffer
      return cloneArrayBuffer(object)

    case boolTag://Boolean
    case dateTag://Date
      return new Ctor(+object)

    case dataViewTag://DataView
      return cloneDataView(object, isDeep)

    case float32Tag: case float64Tag:
    case int8Tag: case int16Tag: case int32Tag:
    case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
    //Float32Array Float64Array Int8Array Int16Array Int32Array Uint8Array Uint8ClampedArray Uint16Array Uint32Array
      return cloneTypedArray(object, isDeep)

    case mapTag://Map
      return new Ctor

    case numberTag:
    case stringTag:
    //Number String
      return new Ctor(object)

    case regexpTag://RegExp
      return cloneRegExp(object)

    case setTag://Set
      return new Ctor

    case symbolTag://Symbol
      return cloneSymbol(object)
  }
}

/**
 * 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)//初始化结果数组

  // Add properties assigned by `RegExp#exec`.
  //将正则方法exec()返回的数组的index和input属性克隆到结果数组上
  if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
    result.index = array.index
    result.input = array.input
  }
  return result
}

/**
 * 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`.
 * @param {Object} [object] The parent object of `value`.
 * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
 * @returns {*} Returns the cloned value.
 */
//clone和cloneDeep的基础实现
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) {//如果自定义克隆处理后能够result有变化,直接返回结果
    return result
  }
  if (!isObject(value)) {//判断要克隆的值是否不是对象是简单值,如果是简单值直接返回
    return value
  }
  const isArr = Array.isArray(value)//判断value是否是array
  const tag = getTag(value)//获取value的toStringTag
  if (isArr) {//如果value是数组
    result = initCloneArray(value)
    //初始化克隆数组,返回一个初始化的克隆结果,就是和原数组长度一样的但是元素都为空位的数组
    if (!isDeep) {//如果是浅克隆,调用copyArray处理
      return copyArray(value, result)
    }
  } else {//如果value不是数组
    const isFunc = typeof value == 'function'//判断value是否是function类型

    if (isBuffer(value)) {//如果value是buffer对象
      return cloneBuffer(value, isDeep)//使用cloneBuffer克隆buffer对象
    }
    if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
      //如果value的类型是Object arguments 或者是函数并且没有父级对象包裹
      result = (isFlat || isFunc) ? {} : initCloneObject(value)
      //初始化克隆结果,如果需要展开继承属性或者value是function,那初始化为空对象,否则调用initCloneObject处理
      if (!isDeep) {//如果不是深度克隆
        //先将普通属性克隆,然后再克隆symbol属性
        return isFlat
          ? copySymbolsIn(value, baseAssignIn(result, value))
          : copySymbols(value, baseAssign(result, value))
      }
    } else {
      if (isFunc || !cloneableTags[tag]) {
        //其他情况如果是有父级对象的function或者不支持克隆的类型
        return object ? value : {}//返回value或者空对象
      }
      result = initCloneByTag(value, tag, isDeep)
      //根据toStringTag来初始化克隆
    }
  }
  // Check for circular references and return its corresponding clone.
  //检查循环引用并且返回对应的克隆
  stack || (stack = new Stack)//用来存放键值对的数据结构
  const stacked = stack.get(value)//获取stack中的value对应的result
  if (stacked) {//如果stack中存在直接返回
    return stacked
  }
  stack.set(value, result)//设置value对应result到stack中

  if (tag == mapTag) {//如果是map数据类型
    value.forEach((subValue, key) => {//循环map复制到result上,递归调用baseClone复制其中的值
      result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack))
    })
    return result
  }

  if (tag == setTag) {//如果是set数据类型
    value.forEach((subValue) => {//循环set复制到result上,递归调用baseClone复制其中的值
      result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack))
    })
    return result
  }

  if (isTypedArray(value)) {//如果是typedArray直接返回结果
    return result
  }

  const keysFunc = isFull
    ? (isFlat ? getAllKeysIn : getAllKeys)
    : (isFlat ? keysIn : keys)//获取对象key数组的方法根据是否需要展平继承属性使用不同的

  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
}

export default baseClone