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
函数的实现思路可以概括为:
-
检查输入值是否为对象类型
- 如果不是对象,直接调用
nativeKeysIn
获取所有属性名 - 这是一种优化,避免对非对象类型进行复杂处理
- 如果不是对象,直接调用
-
如果是对象,判断是否为原型对象
- 记录这个信息,后续用于处理 constructor 属性
-
使用 for...in 循环遍历对象的所有可枚举属性(包括继承的)
- 特殊处理 constructor 属性,避免在某些情况下返回它
- 将符合条件的属性名添加到结果数组
-
返回包含所有收集到的属性名的数组
这种实现在保证获取完整属性的同时,处理了 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);
}
}
这段代码是函数的核心,它做了以下工作:
- 使用
for...in
循环遍历对象的所有可枚举属性(包括继承的) - 对每个属性名
key
进行条件检查,排除以下情况:- 属性名为
'constructor'
且满足以下条件之一:object
是原型对象(isProto
为 true)- 或者
constructor
不是object
的自身属性
- 属性名为
- 将通过检查的属性名添加到
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 的对比
baseKeysIn
与 baseKeys
函数的主要区别在于是否包含继承的属性:
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;
}
主要区别:
baseKeys
使用hasOwnProperty
过滤出自身属性,而baseKeysIn
不过滤继承属性baseKeys
总是排除constructor
属性,而baseKeysIn
只在特定情况下排除baseKeys
对非原型对象使用nativeKeys
(基于Object.keys
),而baseKeysIn
对非对象使用nativeKeysIn
总结
baseKeysIn
是 Lodash 中用于获取对象所有可枚举属性名(包括继承的)的核心实现函数。