目录

Lodash源码阅读-getSymbolsIn

Lodash 源码阅读-getSymbolsIn

概述

getSymbolsIn 是 Lodash 库中的一个内部工具函数,它的作用是创建一个包含目标对象自身和继承的可枚举 Symbol 属性的数组。与 getSymbols 不同,它会遍历整个原型链,获取对象及其所有原型上的 Symbol 属性。

前置学习

依赖函数

  • nativeGetSymbols :原生的 Object.getOwnPropertySymbols 方法的引用
  • stubArray:一个返回空数组的工具函数,在不支持 Symbol 的环境中作为 fallback
  • arrayPush:用于将一个数组的元素追加到另一个数组中的函数
  • getSymbols:获取对象自身可枚举 Symbol 属性的函数
  • getPrototype :获取对象原型的函数,相当于 Object.getPrototypeOf

技术知识

  • ES6 Symbol:JavaScript 中的原始数据类型,表示唯一的标识符
  • 原型链(Prototype Chain):JavaScript 对象之间通过原型链接起来的继承机制
  • Object.getPrototypeOf:获取对象原型的方法

源码实现

javascript 复制代码
/**
 * Creates an array of the own and inherited enumerable symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbolsIn = !nativeGetSymbols
  ? stubArray
  : function (object) {
      var result = [];
      while (object) {
        arrayPush(result, getSymbols(object));
        object = getPrototype(object);
      }
      return result;
    };

实现思路

getSymbolsIn 函数的实现思路非常清晰:

  1. 首先判断环境是否支持 Symbol(通过检查 nativeGetSymbols 是否存在):

    • 如果不支持,简单返回 stubArray 函数,即返回空数组
    • 如果支持,则定义一个函数,该函数会遍历对象及其原型链
  2. 对于支持 Symbol 的环境,实现逻辑如下:

    • 创建一个空数组 result 用于存储结果
    • 使用 while 循环遍历对象及其原型链
    • 在每次循环中,使用 getSymbols 获取当前对象的可枚举 Symbol 属性,并通过 arrayPush 添加到结果数组中
    • 使用 getPrototype 获取当前对象的原型,作为下一次循环的对象
    • 当原型链遍历完毕(到达 null)时,循环结束
    • 返回收集到的所有 Symbol 属性数组

这种实现方式确保了可以获取对象及其整个原型链上的所有可枚举 Symbol 属性。

源码解析

条件定义部分

javascript 复制代码
var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {

这行代码使用条件运算符来定义 getSymbolsIn 函数。它检查 nativeGetSymbols(即 Object.getOwnPropertySymbols)是否存在:

  • 如果 nativeGetSymbols 不存在(值为 falsy),则 getSymbolsIn 被赋值为 stubArray 函数,始终返回空数组
  • 如果 nativeGetSymbols 存在,则 getSymbolsIn 被赋值为一个新定义的函数

这种模式在 Lodash 中很常见,用于处理不同环境的兼容性问题。在不支持 Symbol 的环境中,提供一个简单的替代实现。

结果初始化

javascript 复制代码
var result = [];

这行代码创建了一个空数组,用于存储收集到的所有 Symbol 属性。这是必要的,因为函数需要返回一个包含对象及其原型链上所有 Symbol 属性的数组。

原型链遍历

javascript 复制代码
while (object) {
  arrayPush(result, getSymbols(object));
  object = getPrototype(object);
}

这段代码是函数的核心部分,它使用 while 循环来遍历对象及其原型链:

  1. while (object) 检查当前对象是否存在。当遍历到原型链的顶端(null)时,循环结束
  2. arrayPush(result, getSymbols(object)) 调用 getSymbols 获取当前对象的可枚举 Symbol 属性,并使用 arrayPush 将这些属性添加到结果数组中
  3. object = getPrototype(object) 获取当前对象的原型,作为下一次循环的对象

这种循环方式可以有效地遍历整个原型链,收集所有继承的 Symbol 属性。

javascript 复制代码
// arrayPush 函数的实现
function arrayPush(array, values) {
  var index = -1,
    length = values.length,
    offset = array.length;

  while (++index < length) {
    array[offset + index] = values[index];
  }
  return array;
}

// getPrototype 函数的实现 (简化版)
var getPrototype = Object.getPrototypeOf;

返回结果

javascript 复制代码
return result;

函数最后返回收集到的所有 Symbol 属性数组。这个数组包含了对象自身和其原型链上的所有可枚举 Symbol 属性。

与 getSymbols 的比较

getSymbolsIngetSymbols 是两个相似但用途不同的函数:

  1. 属性范围

    • getSymbols 只返回对象自身的可枚举 Symbol 属性
    • getSymbolsIn 返回对象自身和继承的可枚举 Symbol 属性
  2. 实现方式

    • getSymbols 直接使用 Object.getOwnPropertySymbols 并过滤可枚举属性
    • getSymbolsIn 使用循环遍历原型链,并在每个层级上调用 getSymbols
  3. 使用场景

    • getSymbols 适用于只关心对象自身 Symbol 属性的情况
    • getSymbolsIn 适用于需要考虑继承 Symbol 属性的情况

示例:

javascript 复制代码
const parent = {};
const child = Object.create(parent);

// 定义 Symbol 属性
const parentSym = Symbol("parent");
const childSym = Symbol("child");

// 在原型上设置 Symbol 属性
parent[parentSym] = "parent value";
// 在对象自身设置 Symbol 属性
child[childSym] = "child value";

// 使用 getSymbols
console.log(getSymbols(child).length); // 1,只有 childSym

// 使用 getSymbolsIn
console.log(getSymbolsIn(child).length); // 2,包括 childSym 和继承的 parentSym

总结

getSymbolsIn 是 Lodash 中一个重要的内部工具函数,它通过遍历原型链获取对象及其所有原型上的可枚举 Symbol 属性。这个函数在处理需要考虑继承属性的场景中非常有用,如完整的对象克隆和合并操作。

函数的实现体现了几个重要的软件工程原则:

  1. 优雅降级:在不支持 Symbol 的环境中提供合理的替代方案
  2. 循环迭代:通过简单的循环实现对原型链的有效遍历
  3. 函数组合 :基于 getSymbolsarrayPushgetPrototype 等基础函数构建更复杂的功能
  4. 职责明确:专注于获取对象及其原型链上的 Symbol 属性这一单一任务

在需要处理 JavaScript 对象继承结构和 Symbol 属性的场景中,getSymbolsIn 提供了一种简洁而有效的解决方案。

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
你的人类朋友6 分钟前
浅谈Object.prototype.hasOwnProperty.call(a, b)
javascript·后端·node.js
Mintopia11 分钟前
深入理解 Three.js 中的 Mesh:构建 3D 世界的基石
前端·javascript·three.js
打瞌睡de喵14 分钟前
JavaScript 空对象检测
javascript
前端太佬15 分钟前
暂时性死区(Temporal Dead Zone, TDZ)
前端·javascript·node.js
Mintopia17 分钟前
Node.js 中 http.createServer API 详解
前端·javascript·node.js
艾克马斯奎普特22 分钟前
Vue.js 3 渐进式实现之响应式系统——第三节:建立副作用函数与被操作字段之间的联系
javascript·vue.js
xRainco22 分钟前
Redux从简单到进阶(Redux、React-redux、Redux-toolkit)
前端
印第安老斑鸠啊23 分钟前
由一次CI流水线失败引发的对各类构建工具的思考
前端
CodePencil25 分钟前
CSS专题之外边距重叠
前端·css
hepherd27 分钟前
Flask学习笔记 - 表单
前端·flask