Lodash源码阅读-baseHasIn

Lodash 源码阅读-baseHasIn

概述

baseHasIn 是 Lodash 内部的一个小工具函数,它的工作很简单:判断一个对象里面有没有某个属性,不管是自己直接拥有的还是从"父辈"那里继承来的。它是公开 API _.hasIn 的内部实现,不支持多层路径查询(比如 "a.b.c"),只能查询直接属性。

前置学习

依赖知识

  • JavaScript 原型链:这是 JS 中继承的实现方式,子对象可以使用父对象的属性和方法
  • in 操作符 :JS 里的一个操作符,用来检查一个属性是否存在于对象或其原型链中,比如 'name' in person
  • Object 构造函数 :JS 内置的函数,可以把各种值包装成对象,比如 Object(123) 会返回一个数字对象

相关函数

  • baseHasbaseHasIn 的"堂兄弟",只查自己家的东西,不管继承来的
  • 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 的实现超级简单,仅仅一行代码,但这一行里面藏着三个关键点:

  1. 空值检查 :首先确保对象不是 nullundefined,防止程序崩溃。

  2. 包装成对象 :用 Object(object) 把任何值转成对象。这样即使你传入数字或字符串这样的基本类型,也能正常工作。

  3. 用 in 操作符 :使用 JS 的 in 操作符来查找,这个操作符会查找整个原型链。这就是为什么 baseHasIn 能找到继承的属性,而不仅仅是自己的属性。

这三点结合起来,让 baseHasIn 能够安全、正确地检查一个属性是否可以被对象访问到,无论这个属性是直接拥有的还是继承的。

源码解析

函数参数

javascript 复制代码
function baseHasIn(object, key) {

这个函数接收两个参数:

  • object:要查询的对象,可以是任何东西,甚至是数字或字符串
  • key:要检查的属性名,通常是字符串,比如 "name"、"age"

实现代码

javascript 复制代码
return object != null && key in Object(object);

这短短一行代码做了三件事:

  1. object != null:检查 object 是否有值(不是 null 也不是 undefined)。这用了一个小技巧:在 JS 中,null == undefinedtrue,所以这一个判断可以同时排除这两种空值。

    javascript 复制代码
    null == null; // true
    undefined == null; // true
    123 == null; // false
  2. Object(object):把输入值转换为对象。这个操作对不同类型的值有不同效果:

    • 如果已经是对象,保持不变
    • 如果是数字,变成 Number 对象
    • 如果是字符串,变成 String 对象
    • 如果是布尔值,变成 Boolean 对象
    javascript 复制代码
    Object(123); // Number {123}
    Object("hello"); // String {"hello"}
    Object(true); // Boolean {true}
    Object({ a: 1 }); // {a: 1} (不变)
  3. 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 构造函数,让我们能够安全、方便地检查对象的属性,无论是自己的还是继承的。

通过这个小小的函数,我们可以学到几个重要的编程技巧:

  1. 安全检查 :先检查 nullundefined,避免程序崩溃
  2. 类型转换 :使用 Object() 包装器统一处理不同类型的输入
  3. 原型链查找 :利用 in 操作符查找整个原型链,而不仅仅是对象自身

这些技巧不仅适用于属性检查,在很多其他场景中也非常有用。当我们需要处理不确定来源的数据、考虑继承关系,或者创建灵活的 API 时,hasIn 这样的工具函数就能派上大用场。

最后,虽然现代 JavaScript 引入了可选链操作符 ?. 和空值合并操作符 ??,但在处理继承属性和更复杂的场景时,hasIn 仍然有其独特的价值。

相关推荐
njsgcs11 分钟前
opencascade.js stp vite webpack 调试笔记
开发语言·前端·javascript
T0uken1 小时前
【前端】:单 HTML 去除 Word 批注
前端·html·word
st紫月1 小时前
用vue和go实现登录加密
前端·vue.js·golang
岁岁岁平安1 小时前
Vue3学习(组合式API——计算属性computed详解)
前端·javascript·vue.js·学习·computed·计算属性
HWL56792 小时前
Express项目解决跨域问题
前端·后端·中间件·node.js·express
刺客-Andy2 小时前
React 第三十九节 React Router 中的 unstable_usePrompt Hook的详细用法及案例
前端·javascript·react.js
Go_going_2 小时前
【js基础笔记] - 包含es6 类的使用
前端·javascript·笔记
浩~~3 小时前
HTML5 浮动(Float)详解
前端·html·html5
AI大模型顾潇4 小时前
[特殊字符] 本地大模型编程实战(29):用大语言模型LLM查询图数据库NEO4J(2)
前端·数据库·人工智能·语言模型·自然语言处理·prompt·neo4j
九月TTS4 小时前
TTS-Web-Vue系列:Vue3实现内嵌iframe文档显示功能
前端·javascript·vue.js