Lodash源码阅读-getSymbols

Lodash 源码阅读-getSymbols

功能概述

getSymbols 是 Lodash 库中的一个内部工具函数,它的主要作用是创建一个包含目标对象自身可枚举 Symbol 属性的数组。这个函数在处理对象的 Symbol 类型属性时非常有用,特别是在需要获取对象所有键(包括 Symbol 类型)的场景中。

前置学习

依赖关系

getSymbols 函数依赖以下几个函数和变量:

  • nativeGetSymbols :原生的 Object.getOwnPropertySymbols 方法的引用
  • stubArray:一个返回空数组的工具函数,在不支持 Symbol 的环境中作为 fallback
  • arrayFilter:一个数组过滤函数,用于筛选出可枚举的 Symbol
  • propertyIsEnumerableObject.prototype.propertyIsEnumerable 方法的引用,用于检查属性是否可枚举

技术知识

  • ES6 Symbol:JavaScript 中的原始数据类型,表示唯一的标识符
  • Object.getOwnPropertySymbols:获取对象自身所有 Symbol 属性的方法
  • 可枚举性(Enumerability):JavaScript 属性的一个特性,决定属性是否出现在对象的枚举中
  • 条件定义:根据环境特性动态定义函数的技术

源码实现

javascript 复制代码
/**
 * Creates an array of the own enumerable symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbols = !nativeGetSymbols
  ? stubArray
  : function (object) {
      if (object == null) {
        return [];
      }
      object = Object(object);
      return arrayFilter(nativeGetSymbols(object), function (symbol) {
        return propertyIsEnumerable.call(object, symbol);
      });
    };

实现思路

getSymbols 函数的实现思路非常巧妙,它首先检查环境是否支持 Symbol(通过检查 nativeGetSymbols 是否存在),然后根据结果采取不同的策略:

  1. 如果环境不支持 Symbol,则直接使用 stubArray 函数,始终返回空数组
  2. 如果环境支持 Symbol,则定义一个函数,该函数会:
    • 检查输入对象是否为 null 或 undefined,如果是则返回空数组
    • 将输入转换为对象
    • 使用 nativeGetSymbols 获取所有 Symbol 属性
    • 使用 arrayFilter 过滤出可枚举的 Symbol 属性
    • 返回过滤后的结果

这种实现方式既保证了在所有环境中的兼容性,又提供了在支持 Symbol 的环境中的完整功能。

源码解析

条件定义

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

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

  • 如果 nativeGetSymbols 不存在(值为 falsy),则 getSymbols 被赋值为 stubArray 函数
  • 如果 nativeGetSymbols 存在,则 getSymbols 被赋值为一个新定义的函数

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

空值处理

javascript 复制代码
if (object == null) {
  return [];
}

这段代码检查输入对象是否为 nullundefined(使用 == 运算符可以同时检查这两种情况)。如果是,则直接返回空数组,避免后续操作出错。

对象转换

javascript 复制代码
object = Object(object);

这行代码将输入值转换为对象。这是一个安全措施,确保即使传入的是原始值(如数字或字符串),也能正确处理。例如:

  • Object(42) 会创建一个 Number 对象
  • Object('hello') 会创建一个 String 对象
  • 如果 object 已经是对象,则不会有变化

获取和过滤 Symbol

javascript 复制代码
return arrayFilter(nativeGetSymbols(object), function (symbol) {
  return propertyIsEnumerable.call(object, symbol);
});

这段代码是函数的核心部分:

  1. nativeGetSymbols(object) 调用原生的 Object.getOwnPropertySymbols 方法,获取对象的所有 Symbol 属性(无论是否可枚举)
  2. arrayFilter 函数遍历这些 Symbol,并应用一个过滤函数
  3. 过滤函数 function(symbol) { return propertyIsEnumerable.call(object, symbol); } 检查每个 Symbol 是否是对象的可枚举属性
  4. 最终返回只包含可枚举 Symbol 的数组

这里使用 propertyIsEnumerable.call(object, symbol) 而不是 object.propertyIsEnumerable(symbol) 是为了确保正确的 this 绑定,特别是在处理原始值包装对象的情况下。

stubArray 函数

当环境不支持 Symbol 时,getSymbols 会使用 stubArray 函数:

javascript 复制代码
function stubArray() {
  return [];
}

这个函数非常简单,它只是返回一个空数组。在不支持 Symbol 的环境中,没有 Symbol 属性可以获取,所以返回空数组是合理的行为。

与原生方法的比较

getSymbols 与原生的 Object.getOwnPropertySymbols 方法有一些重要区别:

  1. 可枚举性过滤

    • getSymbols 只返回可枚举的 Symbol 属性
    • Object.getOwnPropertySymbols 返回所有 Symbol 属性,无论是否可枚举
    javascript 复制代码
    const obj = {};
    const sym1 = Symbol("enumerable");
    const sym2 = Symbol("non-enumerable");
    
    Object.defineProperty(obj, sym1, { enumerable: true, value: "value1" });
    Object.defineProperty(obj, sym2, { enumerable: false, value: "value2" });
    
    console.log(Object.getOwnPropertySymbols(obj).length); // 2
    console.log(_.getSymbols(obj).length); // 1
  2. 兼容性处理

    • getSymbols 在不支持 Symbol 的环境中优雅降级,返回空数组
    • Object.getOwnPropertySymbols 在不支持的环境中会抛出错误
  3. 空值处理

    • getSymbols 对 null 和 undefined 返回空数组
    • Object.getOwnPropertySymbols 对 null 和 undefined 会抛出 TypeError
    javascript 复制代码
    try {
      Object.getOwnPropertySymbols(null); // 抛出 TypeError
    } catch (e) {
      console.error(e);
    }
    
    console.log(_.getSymbols(null)); // []

总结

getSymbols 是 Lodash 中一个精心设计的内部工具函数,它解决了获取对象可枚举 Symbol 属性的问题,并提供了良好的兼容性和错误处理。

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

  1. 优雅降级:在不支持新特性的环境中提供合理的替代方案
  2. 防御性编程:通过检查空值和类型转换,防止运行时错误
  3. 单一职责:函数只负责一件事情 - 获取对象的可枚举 Symbol 属性
  4. 组合复用 :通过组合使用其他函数(如 arrayFilterstubArray)来实现功能

在现代 JavaScript 开发中,随着 Symbol 的广泛使用,特别是在库和框架内部,getSymbols 这样的工具函数变得越来越重要。它帮助开发者处理包含 Symbol 的对象,确保在操作对象时不会遗漏这些特殊属性。

相关推荐
掘金安东尼7 分钟前
上周前端发生哪些新鲜事儿? #407
前端·面试·github
小谭鸡米花16 分钟前
ECharts各类炫酷图表/3D柱形图
前端·javascript·echarts·大屏端
郝晨妤21 分钟前
【鸿蒙5.0】向用户申请麦克风授权
linux·服务器·前端·华为·harmonyos·鸿蒙
神秘代码行者32 分钟前
使用 contenteditable 属性实现网页内容可编辑化
前端·html5
小鱼人爱编程33 分钟前
Look My Eyes 最新IDEA快速搭建Java Web工程的两种方式
java·前端·后端
郝晨妤34 分钟前
【鸿蒙5.0】鸿蒙登录界面 web嵌入(隐私页面加载)
前端·华为·harmonyos
小鱼人爱编程39 分钟前
当上小组长的第3天,我裁掉了2年老员工
前端·后端·面试
晓得迷路了40 分钟前
栗子前端技术周刊第 74 期 - 2025 Vue.js 现状报告、Element Plus X、Material UI v7...
前端·javascript·vue.js
知识分享小能手44 分钟前
CSS3学习教程,从入门到精通, CSS3 变形效果(2D 和 3D)的详细语法知识点及案例代码(22)
前端·javascript·css·学习·3d·css3·html5
花之亡灵1 小时前
.net 6 + vue3中使用SignaIR实现双向通信功能
前端·javascript·笔记·websocket·.net·信息与通信