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

相关推荐
zengyuhan5039 分钟前
Windows BLE 开发指南(Rust windows-rs)
前端·rust
醉方休12 分钟前
Webpack loader 的执行机制
前端·webpack·rust
前端老宋Running21 分钟前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔21 分钟前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户44455436542623 分钟前
Android的自定义View
前端
WILLF24 分钟前
HTML iframe 标签
前端·javascript
枫,为落叶41 分钟前
Axios使用教程(一)
前端
小章鱼学前端1 小时前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js
ohyeah1 小时前
JavaScript 词法作用域、作用域链与闭包:从代码看机制
前端·javascript
流星稍逝1 小时前
手搓一个简简单单进度条
前端