Lodash源码阅读-baseKeysIn

Lodash 源码阅读-baseKeysIn

功能概述

baseKeysIn 是 Lodash 中的一个内部工具函数,用于获取对象的所有可枚举属性名(包括自身和继承的属性)。它是 _.keysIn 方法的基础实现,能够处理各种类型的对象,并根据对象的类型采用不同的处理策略。

baseKeys(只返回对象自身属性)不同,baseKeysIn 会返回对象自身的可枚举属性以及从原型链继承的可枚举属性。

前置学习

依赖函数

  • isObject:检查值是否为对象类型
  • nativeKeysIn:通过 for...in 循环获取对象所有可枚举属性的工具函数
  • isPrototype:检查值是否为原型对象

技术知识

  • 可枚举属性:JavaScript 对象属性的可枚举性及其对遍历的影响
  • 原型链:JavaScript 的继承机制,以及如何访问原型链上的属性
  • for...in 循环:遍历对象所有可枚举属性(包括继承的)的 JavaScript 语法
  • constructor 属性:对象原型上的特殊属性,需要特殊处理
  • hasOwnProperty:区分对象自身属性和继承属性的方法

源码实现

javascript 复制代码
function baseKeysIn(object) {
  if (!isObject(object)) {
    return nativeKeysIn(object);
  }
  var isProto = isPrototype(object),
    result = [];

  for (var key in object) {
    if (
      !(key == "constructor" && (isProto || !hasOwnProperty.call(object, key)))
    ) {
      result.push(key);
    }
  }
  return result;
}

实现思路

baseKeysIn 函数的实现思路可以概括为:

  1. 检查输入值是否为对象类型

    • 如果不是对象,直接调用 nativeKeysIn 获取所有属性名
    • 这是一种优化,避免对非对象类型进行复杂处理
  2. 如果是对象,判断是否为原型对象

    • 记录这个信息,后续用于处理 constructor 属性
  3. 使用 for...in 循环遍历对象的所有可枚举属性(包括继承的)

    • 特殊处理 constructor 属性,避免在某些情况下返回它
    • 将符合条件的属性名添加到结果数组
  4. 返回包含所有收集到的属性名的数组

这种实现在保证获取完整属性的同时,处理了 constructor 属性的特殊情况,提高了函数的健壮性。

源码解析

让我们逐行分析 baseKeysIn 函数的实现:

非对象类型处理

javascript 复制代码
if (!isObject(object)) {
  return nativeKeysIn(object);
}
  • 使用 isObject 检查 object 是否为对象类型
  • 如果不是对象(如原始类型、null、undefined),直接调用 nativeKeysIn 函数
  • nativeKeysIn 会使用 for...in 循环获取所有可枚举属性名

示例:

javascript 复制代码
// 非对象类型的处理
console.log(_.keysIn("hello")); // ['0', '1', '2', '3', '4']
console.log(_.keysIn(42)); // []
console.log(_.keysIn(null)); // []

变量初始化

javascript 复制代码
var isProto = isPrototype(object),
  result = [];
  • 使用 isPrototype 检查 object 是否为原型对象,结果存储在 isProto 变量中
  • 创建一个空数组 result,用于存储收集到的属性名

示例:

javascript 复制代码
// 检查是否为原型对象
function Person() {}
const proto = Person.prototype;
console.log(isPrototype(proto)); // true

const obj = { a: 1 };
console.log(isPrototype(obj)); // false

属性遍历和过滤

javascript 复制代码
for (var key in object) {
  if (
    !(key == "constructor" && (isProto || !hasOwnProperty.call(object, key)))
  ) {
    result.push(key);
  }
}

这段代码是函数的核心,它做了以下工作:

  1. 使用 for...in 循环遍历对象的所有可枚举属性(包括继承的)
  2. 对每个属性名 key 进行条件检查,排除以下情况:
    • 属性名为 'constructor' 且满足以下条件之一:
      • object 是原型对象(isProto 为 true)
      • 或者 constructor 不是 object 的自身属性
  3. 将通过检查的属性名添加到 result 数组

这个条件设计得很巧妙,主要用于处理 constructor 属性的特殊情况:

  • 对于原型对象,避免返回 constructor 属性,因为它通常不是用户定义的数据属性
  • 对于普通对象,如果 constructor 是继承的而非自身属性,也避免返回它

示例:

javascript 复制代码
// constructor 属性的处理
function Person() {}
Person.prototype.sayHello = function () {};
// 原型对象上有 constructor 和 sayHello 属性
console.log(_.keysIn(Person.prototype)); // ['sayHello']  (constructor 被排除)

// 如果用户在原型上自定义了 constructor
Person.prototype.constructor = CustomConstructor;
console.log(_.keysIn(Person.prototype)); // ['sayHello', 'constructor']

返回结果

javascript 复制代码
return result;
  • 返回包含所有收集到的属性名的数组

与 baseKeys 的对比

baseKeysInbaseKeys 函数的主要区别在于是否包含继承的属性:

javascript 复制代码
// baseKeys 只返回对象自身的可枚举属性
function baseKeys(object) {
  if (!isPrototype(object)) {
    return nativeKeys(object);
  }
  var result = [];
  for (var key in Object(object)) {
    if (hasOwnProperty.call(object, key) && key != "constructor") {
      result.push(key);
    }
  }
  return result;
}

主要区别:

  1. baseKeys 使用 hasOwnProperty 过滤出自身属性,而 baseKeysIn 不过滤继承属性
  2. baseKeys 总是排除 constructor 属性,而 baseKeysIn 只在特定情况下排除
  3. baseKeys 对非原型对象使用 nativeKeys(基于 Object.keys),而 baseKeysIn 对非对象使用 nativeKeysIn

总结

baseKeysIn 是 Lodash 中用于获取对象所有可枚举属性名(包括继承的)的核心实现函数。

相关推荐
weixin_431600443 分钟前
NestJS 入门(7):生命周期钩子——构造函数和 `OnModuleInit` 差在哪?
前端·后端·学习·node.js·nest.js
cindershade1 小时前
用原生 HTML5 视频与 Canvas 实现浏览器端视频抽帧封面
前端
笨鸟先飞,勤能补拙1 小时前
Web 服务器与计算机网络全景原理
运维·服务器·前端·网络·人工智能·计算机网络·web安全
NutShell Wang1 小时前
Wails v3 Beta 实战:用显式对象模型重写你的第一个 Go 桌面应用
前端·人工智能·go·vibe coding
daols881 小时前
vue 甘特图 vxe-gantt 任务条拖拽自动更新日期
javascript·vue.js·甘特图
cindershade1 小时前
前端国际化工程实践:语言包拆分、动态加载与日期数字格式统一
前端
kisbad1 小时前
Day 036|OpenAI Agents SDK 快速开始:今天跑通第一个 Agent
java·前端·javascript
qq_267612891 小时前
Gitee CodePecker行业场景化落地指南:金融、车联网与IoT双引擎选型与工程化路径
前端·gitee·自动化
初晨未凉1 小时前
elementui自定义内容图片预览
前端
无糖可可果3 小时前
从零读懂一个 Next.js 全栈笔记应用
前端