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 函数的场景下。

相关推荐
IT_陈寒4 分钟前
Vite 5个隐藏技巧让你的项目构建速度提升50%,第3个太香了!
前端·人工智能·后端
詩句☾⋆᭄南笙12 分钟前
HTML的盒子模型
前端·html·盒子模型
落言14 分钟前
AI 时代的工程师:懂,却非懂的时代
前端·程序员·架构
一枚攻城狮16 分钟前
前端知识点大汇总
前端
余道各努力,千里自同风1 小时前
el-input 输入框宽度自适应宽度
javascript·vue.js·elementui
Mike_jia1 小时前
DumbAssets:开源资产管理神器,家庭与企业的高效管家
前端
Southern Wind2 小时前
Vue 3 多实例 + 缓存复用:理念及实践
前端·javascript·vue.js·缓存·html
HuangYongbiao2 小时前
Rspack 原理:webpack,我为什么不要你
前端
yinuo2 小时前
前端项目开发阶段崩溃?试试这招“Node 内存扩容术”,立马复活!
前端
前端鳄鱼崽2 小时前
【react-native-inspector】全网唯一开源 react-native 点击组件跳转到编辑器
前端·react native·react.js