Lodash 源码阅读-nativeKeysIn
功能概述
nativeKeysIn 是 Lodash 中的一个内部工具函数,用于获取对象的所有可枚举属性名(包括自身和继承的属性)。它是 _.keysIn 方法的基础实现之一,通过原生的 for...in 循环遍历对象的所有可枚举属性,包括从原型链继承的属性。
与 nativeKeys(基于 Object.keys 实现)不同,nativeKeysIn 采用了更简单直接的方式,直接利用 JavaScript 的 for...in 循环来获取对象的所有可枚举属性,无论是自身的还是继承的。
前置学习
依赖函数
虽然 nativeKeysIn 本身没有直接依赖其他函数,但理解以下函数有助于更全面地理解它在 Lodash 中的定位:
baseKeysIn:获取对象所有可枚举属性名的基础实现,会根据对象类型调用nativeKeysInkeysIn:对外暴露的 API,用于获取对象自身和继承的可枚举属性名
内部调用链:keysIn → baseKeysIn → nativeKeysIn
技术知识
- for...in 循环:JavaScript 中用于遍历对象所有可枚举属性(包括继承属性)的循环结构
- 可枚举属性 :JavaScript 对象属性的一个特性,决定属性是否会出现在
for...in循环中 - 原型链:JavaScript 对象继承机制的基础,理解原型链对于理解属性继承至关重要
- Object 构造函数:用于将原始值转换为对象,确保安全地访问属性
源码实现
javascript
function nativeKeysIn(object) {
var result = [];
if (object != null) {
for (var key in Object(object)) {
result.push(key);
}
}
return result;
}
实现思路
nativeKeysIn 函数的实现思路非常直接:
- 创建一个空数组,用于存储属性名
- 判断输入对象是否为 null 或 undefined,如果是则直接返回空数组
- 使用
for...in循环遍历对象的所有可枚举属性(包括继承的) - 将每个属性名添加到结果数组中
- 返回最终的属性名数组
这种实现方式利用了 JavaScript 的 for...in 循环特性,该循环会遍历对象自身的可枚举属性以及从原型链继承的可枚举属性,完美符合 keysIn 方法的需求。
源码解析
让我们逐行分析 nativeKeysIn 函数的实现:
函数签名和结果初始化
javascript
function nativeKeysIn(object) {
var result = [];
- 函数接收一个参数
object,可以是任何类型的值 - 创建一个空数组
result,用于存储属性名
空值处理
javascript
if (object != null) {
- 使用宽松比较
!=检查object是否为null或undefined - 这是一种安全防护措施,确保不会在
null或undefined上调用for...in循环
属性遍历和收集
javascript
for (var key in Object(object)) {
result.push(key);
}
Object(object)将输入转换为对象类型,这是一种防御性编程的做法:- 如果
object已经是对象,保持不变 - 如果
object是原始类型(如字符串、数字、布尔值),将其包装为对应的对象类型 - 如果
object是null或undefined(虽然已经在前面检查过),则返回空对象{}
- 如果
for...in循环遍历对象的所有可枚举属性,包括自身的和继承的- 每个属性名通过
push方法添加到result数组中
返回结果
javascript
}
return result;
}
- 返回包含所有收集到的属性名的数组
应用场景
nativeKeysIn 函数主要通过 baseKeysIn 和 keysIn 函数被应用,适用于需要考虑继承属性的场景:
1. 获取对象所有属性(包括继承的)
javascript
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
const foo = new Foo();
console.log(_.keysIn(foo)); // ['a', 'b', 'c']
内部调用链:keysIn → baseKeysIn → nativeKeysIn
注意事项
使用 nativeKeysIn(通过 keysIn)时需要注意以下几点:
-
性能考虑:遍历所有继承属性可能会比仅遍历自身属性慢,特别是对于原型链较长的对象
-
非可枚举属性 :
nativeKeysIn只返回可枚举属性,非可枚举属性不会被收集 -
符号属性 :Symbol 类型的属性会被忽略,因为
for...in循环不会遍历 Symbol 属性 -
属性排序:不同 JavaScript 引擎可能会以不同顺序遍历属性,不要依赖属性的顺序
-
原型污染 :如果原型链被恶意修改(原型污染),
nativeKeysIn可能会返回意外的属性
与 baseKeys 和 nativeKeys 的比较
为了更好地理解 nativeKeysIn,让我们将它与相关函数进行比较:
| 函数 | 获取自身属性 | 获取继承属性 | 实现方式 | 使用场景 |
|---|---|---|---|---|
nativeKeys |
✅ | ❌ | 包装 Object.keys |
仅需要获取自身属性的场景 |
baseKeys |
✅ | ❌ | 使用 nativeKeys 或手动实现 |
处理普通对象和原型对象 |
nativeKeysIn |
✅ | ✅ | 使用 for...in 循环 |
需要获取所有属性的场景 |
baseKeysIn |
✅ | ✅ | 使用 nativeKeysIn 或手动实现 |
处理不同类型的对象 |
总结
nativeKeysIn 函数是 Lodash 中的一个简单而强大的工具函数,用于获取对象的所有可枚举属性名,包括从原型链继承的属性。它的主要特点包括:
- 简单性 :直接利用 JavaScript 的
for...in循环,实现简洁明了 - 完整性:返回对象的所有可枚举属性,包括继承的属性
- 安全性 :通过
Object()和 null 检查,确保处理各种输入类型 - 实用性:在需要处理继承属性的场景中非常有用,如对象合并、深度克隆等