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

相关推荐
万少4 分钟前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL9 分钟前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl0225 分钟前
java web5(黑马)
java·开发语言·前端
Amy.Wang26 分钟前
前端如何实现电子签名
前端·javascript·html5
海天胜景28 分钟前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼29 分钟前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
蓝婷儿31 分钟前
每天一个前端小知识 Day 21 - 浏览器兼容性与 Polyfill 策略
前端
百锦再33 分钟前
Vue中对象赋值问题:对象引用被保留,仅部分属性被覆盖
前端·javascript·vue.js·vue·web·reactive·ref
jingling55537 分钟前
面试版-前端开发核心知识
开发语言·前端·javascript·vue.js·面试·前端框架
拾光拾趣录42 分钟前
CSS 深入解析:提升网页样式技巧与常见问题解决方案
前端·css