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. 类型安全:通过检查对象的具体类型,确保每种类型都得到正确的处理
相关推荐
独立开阀者_FwtCoder2 分钟前
基于 MCP Http SSE模式的天气助手智能体开发实战(一文带你了解MCP两种开发模式)
前端·javascript·后端
月亮慢慢圆14 分钟前
表格单元行合并方法
前端
方阿森15 分钟前
MasterGo + MCP,借助 AI 实现设计稿转代码
前端·ai编程·mcp
逆袭的小黄鸭16 分钟前
一文读懂 JavaScript 的各类继承方式
前端·javascript·面试
谎言西西里18 分钟前
深入浅出 Pinia:革新 Vue 状态管理的利器 ⚡
前端
loooseFish18 分钟前
带分页的docx编辑器 vue3集成canvas-editor
前端
敲代码的玉米C19 分钟前
深入探讨 JavaScript 中的 setTimeout 精确性问题
前端·javascript
XH27620 分钟前
Kotlin infix函数用法详解
前端·kotlin
独立开阀者_FwtCoder20 分钟前
V4 版本发布!强势兼容 Vue、React!
前端·javascript·后端
远游客21 分钟前
菜鸟文章: js运行原理
javascript