Lodash源码阅读-isMasked

功能概述

isMasked 函数用于检测一个函数是否被 core-js 修改过。在 Lodash 的类型检测体系中,它主要配合 isNative 函数使用,用于识别被 polyfill 改写的原生函数。

core-js 简介

core-js 是一个现代化的 JavaScript 标准库,它提供了大量的 polyfill 实现。当我们使用 Babel 等工具转译代码时,通常会引入 core-js 来确保新的 JavaScript 特性在旧版本浏览器中也能正常工作。

例如,当我们在项目中使用 Array.prototype.includes 方法时:

js 复制代码
// 在不支持 includes 的旧版本浏览器中
const arr = [1, 2, 3];
console.log(arr.includes(2)); // 通过 core-js 的 polyfill 使其正常工作

在这种情况下,core-js 会修改原生的 Array.prototype,添加 includes 方法的实现。这就是为什么我们需要一个机制来识别哪些函数是原生的,哪些是被 polyfill 修改过的。

源码实现

js 复制代码
var coreJsData = context["__core-js_shared__"];

var maskSrcKey = (function () {
  var uid = /[^.]+$/.exec(
    (coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO) || ""
  );
  return uid ? "Symbol(src)_1." + uid : "";
})();

function isMasked(func) {
  return !!maskSrcKey && maskSrcKey in func;
}

实现原理解析

原理概述

isMasked 函数通过检查函数对象上是否存在特定的 Symbol 属性来判断该函数是否被 core-js 修改过。这个特定的 Symbol 属性是 core-js 在修改原生函数时添加的标记,通过这个标记我们可以识别出哪些函数被 polyfill 改写过。

为什么需要区分原生函数和 polyfill?

区分原生函数和 polyfill 实现有几个重要原因:

  1. 性能考虑:原生函数通常由浏览器引擎实现,性能更好。
  2. 功能稳定性:原生实现经过更严格的测试,更可靠。
  3. 调试需求:在排查问题时,知道一个函数是原生的还是 polyfill 的可以帮助我们更快定位问题。

例如,在性能敏感的应用中,我们可能会这样使用:

js 复制代码
if (isNative(Array.prototype.sort)) {
  // 使用原生排序
  arr.sort();
} else {
  // 使用自定义的排序实现
  customSort(arr);
}

代码解析

1. core-js 环境检测

js 复制代码
var coreJsData = context["__core-js_shared__"];

通过检查全局对象上是否存在 core-js_shared__ 属性来判断是否使用了 core-js:

  • 如果使用了 core-js,这个属性会存在
  • 如果没有使用 core-js,这个属性为 undefined

示例:

js 复制代码
// 在使用了 core-js 的环境中
console.log(!!coreJsData); // => true
console.log(coreJsData);
// => {
//   version: "3.30.2",
//   keys: {
//     IE_PROTO: "Symbol(IE_PROTO)_dj82n5",
//     // 其他内部使用的键值
//   },
//   store: {
//     // 存储 polyfill 相关的数据
//     "Symbol(src)_1.e6YlpA": true,
//     // 其他数据
//   }
// }

// 在没有使用 core-js 的环境中
console.log(!!coreJsData); // => false
console.log(coreJsData); // => undefined

2. maskSrcKey 的生成

js 复制代码
var maskSrcKey = (function () {
  var uid = /[^.]+$/.exec(
    (coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO) || ""
  );
  return uid ? "Symbol(src)_1." + uid : "";
})();

这段代码通过自执行函数生成一个用于检测的键值。这里使用了三层条件判断来确保安全访问:

  1. coreJsData 判断

    • 作用:检查是否在 core-js 环境中

    • 场景:

      js 复制代码
      // 非 core-js 环境
      console.log(coreJsData); // => undefined
      // core-js 环境
      console.log(coreJsData); // => { version: "3.x.x", ... }
  2. coreJsData.keys 判断

    • 作用:确保存在键值映射对象

    • 场景:

      js 复制代码
      // 老版本 core-js 可能不存在 keys
      console.log(coreJsData.keys); // => undefined
      // 现代版本 core-js
      console.log(coreJsData.keys); // => { /* 键值映射对象 */ }
  3. coreJsData.keys.IE_PROTO 判断

    • 作用:获取 IE 浏览器特定的标识符

    • 场景:

      js 复制代码
      // 非 IE 环境
      console.log(coreJsData.keys.IE_PROTO); // => undefined
      // IE 环境
      console.log(coreJsData.keys.IE_PROTO); // => "Symbol(IE_PROTO)_1.e61anxgnoy"

这种链式条件判断确保了代码在各种环境下的安全执行。让我们看一下正则表达式 /[^.]+$/ 是如何从 IE_PROTO 值中提取标识符的:

js 复制代码
// IE_PROTO 值示例
var ieProtoValue = "Symbol(IE_PROTO)_1.e61anxgnoy";

// 正则表达式 /[^.]+$/ 匹配过程
// 1. [^.]+ 匹配除了点号之外的任意字符(一个或多个)
// 2. $ 确保匹配从字符串末尾开始
var uid = /[^.]+$/.exec(ieProtoValue); // => ["e61anxgnoy"]

// 最终生成的 maskSrcKey
var result = "Symbol(src)_1." + uid[0]; // => "Symbol(src)_1.e61anxgnoy"

这种机制确保了:

  • 如果不是 core-js 环境,直接返回空字符串
  • 如果是老版本 core-js,没有 keys 属性,返回空字符串
  • 如果是 IE 环境,使用特定的 IE_PROTO 值生成标识符

示例:

js 复制代码
// 在使用了 core-js 的环境中
console.log(maskSrcKey); // => "Symbol(src)_1.e61anxgnoy"

// 在没有使用 core-js 的环境中
console.log(maskSrcKey); // => ""

3. 函数标记检测

js 复制代码
function isMasked(func) {
  return !!maskSrcKey && maskSrcKey in func;
}

函数通过两步检查来判断函数是否被修改:

  1. 首先确保 maskSrcKey 存在(说明环境中使用了 core-js)
  2. 然后检查函数对象上是否存在 maskSrcKey 属性

示例:

js 复制代码
// 在使用了 babel-polyfill(依赖 core-js)的环境下
console.log(isMasked(Array.prototype.includes)); // => true
console.log(isMasked(function () {})); // => false

// 在没有使用 core-js 的环境下
console.log(isMasked(Array.prototype.includes)); // => false
console.log(isMasked(function () {})); // => false

总结

isMasked 函数通过巧妙的标记检测机制,帮助我们识别出被 core-js 修改过的函数。这个功能在 Lodash 的类型检测系统中发挥着重要作用,特别是在需要区分原生函数和 polyfill 函数的场景下。

相关推荐
hellokai39 分钟前
React Native新架构源码分析
android·前端·react native
li理1 小时前
鸿蒙应用开发完全指南:深度解析UIAbility、页面与导航的生命周期
前端·harmonyos
去伪存真1 小时前
因为rolldown-vite比vite打包速度快, 所以必须把rolldown-vite在项目中用起来🤺
前端
KubeSphere1 小时前
Kubernetes v1.34 重磅发布:调度更快,安全更强,AI 资源管理全面进化
前端
wifi歪f1 小时前
🎉 Stenciljs,一个Web Components框架新体验
前端·javascript
1024小神1 小时前
如何快速copy复制一个网站,或是将网站本地静态化访问
前端
掘金一周2 小时前
DeepSeek删豆包冲上热搜,大模型世子之争演都不演了 | 掘金一周 8.28
前端·人工智能·后端
moyu842 小时前
前端存储三剑客:Cookie、LocalStorage 与 SessionStorage 全方位解析
前端
不爱说话郭德纲2 小时前
👩‍💼产品姐一句小优化,让我给上百个列表加上一个动态实时计算高度的方法😿😿
前端·vue.js·性能优化
现在没有牛仔了2 小时前
小试牛刀,用electron+vue3做了一个文件归纳程序~
前端·electron