Lodash 源码阅读-baseHasIn
概述
baseHasIn
是 Lodash 内部的一个小工具函数,它的工作很简单:判断一个对象里面有没有某个属性,不管是自己直接拥有的还是从"父辈"那里继承来的。它是公开 API _.hasIn
的内部实现,不支持多层路径查询(比如 "a.b.c"),只能查询直接属性。
前置学习
依赖知识
- JavaScript 原型链:这是 JS 中继承的实现方式,子对象可以使用父对象的属性和方法
- in 操作符 :JS 里的一个操作符,用来检查一个属性是否存在于对象或其原型链中,比如
'name' in person
- Object 构造函数 :JS 内置的函数,可以把各种值包装成对象,比如
Object(123)
会返回一个数字对象
相关函数
- baseHas :
baseHasIn
的"堂兄弟",只查自己家的东西,不管继承来的 - hasIn :基于
baseHasIn
实现的公共 API,支持多层路径查询 - hasPath:专门处理多层属性路径的函数,比如查询 "user.profile.name"
源码实现
javascript
/**
* The base implementation of `_.hasIn` without support for deep paths.
*
* @private
* @param {Object} [object] The object to query.
* @param {Array|string} key The key to check.
* @returns {boolean} Returns `true` if `key` exists, else `false`.
*/
function baseHasIn(object, key) {
return object != null && key in Object(object);
}
实现思路
baseHasIn
的实现超级简单,仅仅一行代码,但这一行里面藏着三个关键点:
-
空值检查 :首先确保对象不是
null
或undefined
,防止程序崩溃。 -
包装成对象 :用
Object(object)
把任何值转成对象。这样即使你传入数字或字符串这样的基本类型,也能正常工作。 -
用 in 操作符 :使用 JS 的
in
操作符来查找,这个操作符会查找整个原型链。这就是为什么baseHasIn
能找到继承的属性,而不仅仅是自己的属性。
这三点结合起来,让 baseHasIn
能够安全、正确地检查一个属性是否可以被对象访问到,无论这个属性是直接拥有的还是继承的。
源码解析
函数参数
javascript
function baseHasIn(object, key) {
这个函数接收两个参数:
object
:要查询的对象,可以是任何东西,甚至是数字或字符串key
:要检查的属性名,通常是字符串,比如 "name"、"age"
实现代码
javascript
return object != null && key in Object(object);
这短短一行代码做了三件事:
-
object != null
:检查object
是否有值(不是null
也不是undefined
)。这用了一个小技巧:在 JS 中,null == undefined
是true
,所以这一个判断可以同时排除这两种空值。javascriptnull == null; // true undefined == null; // true 123 == null; // false
-
Object(object)
:把输入值转换为对象。这个操作对不同类型的值有不同效果:- 如果已经是对象,保持不变
- 如果是数字,变成 Number 对象
- 如果是字符串,变成 String 对象
- 如果是布尔值,变成 Boolean 对象
javascriptObject(123); // Number {123} Object("hello"); // String {"hello"} Object(true); // Boolean {true} Object({ a: 1 }); // {a: 1} (不变)
-
key in Object(object)
:使用in
操作符检查属性。这个操作符会查找整个原型链,而不仅仅是对象自己的属性。javascript"toString" in {}; // true,因为所有对象都从 Object.prototype 继承了 toString 方法 "length" in []; // true,数组有 length 属性 "length" in "hello"; // false,但 "length" in Object("hello") 是 true
baseHas 与 baseHasIn 对比
为了更好理解 baseHasIn
,让我们把它和 baseHas
放在一起看:
javascript
// baseHas 实现
function baseHas(object, key) {
return object != null && hasOwnProperty.call(object, key);
}
// baseHasIn 实现
function baseHasIn(object, key) {
return object != null && key in Object(object);
}
主要区别在于查找范围:
baseHas
使用hasOwnProperty
,只查找对象自己直接拥有的属性,就像只在你自己的口袋里找钥匙baseHasIn
使用in
操作符,查找对象及其原型链上的所有属性,相当于不仅查你自己的口袋,还翻遍全家人的口袋
举个例子:
javascript
var obj = {};
"toString" in obj; // true (继承自 Object.prototype)
obj.hasOwnProperty("toString"); // false (不是自己的属性)
// 用 Lodash 函数
_.hasIn(obj, "toString"); // true (使用 baseHasIn)
_.has(obj, "toString"); // false (使用 baseHas)
总结
baseHasIn
虽然看起来简单(就一行代码!),但它却巧妙地结合了 JavaScript 的 in
操作符和 Object
构造函数,让我们能够安全、方便地检查对象的属性,无论是自己的还是继承的。
通过这个小小的函数,我们可以学到几个重要的编程技巧:
- 安全检查 :先检查
null
和undefined
,避免程序崩溃 - 类型转换 :使用
Object()
包装器统一处理不同类型的输入 - 原型链查找 :利用
in
操作符查找整个原型链,而不仅仅是对象自身
这些技巧不仅适用于属性检查,在很多其他场景中也非常有用。当我们需要处理不确定来源的数据、考虑继承关系,或者创建灵活的 API 时,hasIn
这样的工具函数就能派上大用场。
最后,虽然现代 JavaScript 引入了可选链操作符 ?.
和空值合并操作符 ??
,但在处理继承属性和更复杂的场景时,hasIn
仍然有其独特的价值。