Lodash源码阅读-initCloneByTag

Lodash 源码阅读-initCloneByTag

概述

initCloneByTag 是 Lodash 库中的一个内部工具函数,用于基于对象的 toStringTag 初始化对象的克隆。在 Lodash 的深度克隆系统中,这个函数扮演着关键角色,它负责处理各种特殊对象类型(如 Date、RegExp、Map、Set 等)的克隆初始化过程。该函数可以根据对象的类型标签,选择适当的克隆策略,确保对这些特殊类型对象的正确复制。

前置学习

依赖函数

initCloneByTag 依赖以下几个 Lodash 内部的克隆函数:

  • cloneArrayBuffer:用于克隆 ArrayBuffer 对象
  • cloneDataView:用于克隆 DataView 对象
  • cloneRegExp:用于克隆正则表达式对象
  • cloneSymbol:用于克隆 Symbol 对象
  • cloneTypedArray:用于克隆类型化数组(如 Int8Array, Float32Array 等)

技术知识

  • 对象标签(toStringTag) :JavaScript 中每个对象都有一个内部的 [[Class]] 属性,可以通过 Object.prototype.toString 方法获取,表示为 [object Type]
  • 构造函数 :JavaScript 对象的 constructor 属性,指向创建该对象的构造函数
  • Switch 语句:根据不同的标签值选择不同的克隆策略
  • 特殊对象类型:如 ArrayBuffer、Date、RegExp、Map、Set、Symbol 等 JavaScript 内置对象类型的特性

源码实现

javascript 复制代码
/**
 * 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) {
  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);
  }
}

实现思路

initCloneByTag 函数的核心思路是"分类处理",根据对象的类型标签(toStringTag)来决定使用哪种克隆策略:

  1. 首先获取对象的构造函数,用于后续创建新实例
  2. 使用 switch 语句根据对象的标签类型选择不同的克隆路径:
    • 对于 ArrayBuffer,使用 cloneArrayBuffer 函数
    • 对于布尔值和日期对象,使用其构造函数并传入对象的数值表示
    • 对于 DataView,使用 cloneDataView 函数,并传递深克隆标志
    • 对于各种类型化数组,使用 cloneTypedArray 函数,并传递深克隆标志
    • 对于 Map 和 Set,仅创建空实例(内容复制在其他地方处理)
    • 对于数字和字符串,使用其构造函数创建新实例
    • 对于正则表达式,使用 cloneRegExp 函数
    • 对于 Symbol,使用 cloneSymbol 函数

这种方法的优势在于它模块化了克隆过程,为每种特殊类型提供了专门的处理逻辑,使整个克隆系统更易于维护和扩展。

源码解析

函数签名

javascript 复制代码
function initCloneByTag(object, tag, isDeep) {

函数接收三个参数:

  • object:要克隆的对象
  • tag:对象的类型标签,即 Object.prototype.toString.call(object) 的结果
  • isDeep:可选布尔值,指定是否进行深度克隆

获取构造函数

javascript 复制代码
var Ctor = object.constructor;

首先获取对象的构造函数,后续用于创建新实例。这比直接使用全局构造函数(如 BooleanNumber 等)更安全,因为它能正确处理子类化的情况。

Switch 结构与类型处理

javascript 复制代码
switch (tag) {

函数使用 switch 语句根据对象的标签执行不同的克隆策略。每个 case 对应一种特殊对象类型:

ArrayBuffer 克隆
javascript 复制代码
case arrayBufferTag:
  return cloneArrayBuffer(object);

对于 ArrayBuffer 对象,使用专用的 cloneArrayBuffer 函数,它会创建一个新的 ArrayBuffer 并复制原始数据。

布尔值和日期克隆
javascript 复制代码
case boolTag:
case dateTag:
  return new Ctor(+object);

对于布尔值和日期对象:

  1. 使用一元加号操作符 + 将对象转换为数值表示
  2. 将这个数值传递给对象的构造函数,创建一个新实例
    • 对于布尔值,+ 将其转换为 0 或 1
    • 对于日期对象,+ 将其转换为时间戳
DataView 克隆
javascript 复制代码
case dataViewTag:
  return cloneDataView(object, isDeep);

对于 DataView 对象,使用 cloneDataView 函数,并传递 isDeep 参数以确定是否对底层 ArrayBuffer 进行深度克隆。

类型化数组克隆
javascript 复制代码
case float32Tag: case float64Tag:
case int8Tag: case int16Tag: case int32Tag:
case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
  return cloneTypedArray(object, isDeep);

对于所有类型的类型化数组(TypedArray),使用统一的 cloneTypedArray 函数,并传递 isDeep 参数。

Map 和 Set 克隆
javascript 复制代码
case mapTag:
  return new Ctor;

// ...

case setTag:
  return new Ctor;

对于 Map 和 Set 对象,仅创建空实例而不复制内容。内容的复制通常在外部的克隆函数中处理,这是因为 Map 和 Set 的复制可能涉及到循环引用的处理。

数字和字符串克隆
javascript 复制代码
case numberTag:
case stringTag:
  return new Ctor(object);

对于数字和字符串对象,使用其构造函数并传入原始对象作为参数,创建一个新实例。

正则表达式克隆
javascript 复制代码
case regexpTag:
  return cloneRegExp(object);

对于正则表达式对象,使用专用的 cloneRegExp 函数,它会创建一个新的正则表达式并保留原始的标志和 lastIndex 属性。

Symbol 克隆
javascript 复制代码
case symbolTag:
  return cloneSymbol(object);

对于 Symbol 对象,使用专用的 cloneSymbol 函数,它会创建一个新的包含相同值的 Symbol 对象。

总结

initCloneByTag 是 Lodash 克隆系统中的一个核心组件,它通过分类处理不同的特殊对象类型,为这些对象提供适当的克隆策略。尽管函数本身相对简单,但它的设计体现了以下几个重要的编程原则:

  1. 单一职责原则:函数专注于解决一个问题 - 根据类型标签初始化对象克隆
  2. 开放封闭原则:通过使用 switch 结构,该函数易于扩展以支持新的类型
  3. 依赖注入:通过依赖其他专门的克隆函数,而不是自己实现所有类型的克隆逻辑
  4. 类型安全:通过检查对象的具体类型,确保每种类型都得到正确的处理
相关推荐
前端小巷子14 分钟前
Webpack 5模块联邦
前端·javascript·面试
玲小珑17 分钟前
Next.js 教程系列(十九)图像优化:next/image 与高级技巧
前端·next.js
晓得迷路了18 分钟前
栗子前端技术周刊第 91 期 - 新版 React Compiler 文档、2025 HTML 状态调查、Bun v1.2.19...
前端·javascript·react.js
江城开朗的豌豆24 分钟前
Vue和React中的key:为什么列表渲染必须加这玩意儿?
前端·vue.js·面试
江城开朗的豌豆30 分钟前
前端路由傻傻分不清?route和router的区别,看完这篇别再搞混了!
前端·javascript·vue.js
pengzhuofan33 分钟前
Web开发系列-第0章 Web介绍
前端
小鱼人爱编程42 分钟前
Java基石--反射让你直捣黄龙
前端·spring boot·后端
JosieBook2 小时前
【web应用】如何进行前后端调试Debug? + 前端JavaScript调试Debug?
前端·chrome·debug
LBJ辉2 小时前
2. Webpack 高级配置
前端·javascript·webpack
灵感__idea9 小时前
JavaScript高级程序设计(第5版):好的编程就是掌控感
前端·javascript·程序员