关于lodash的内部方法getNative实现

251 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

前言

获取原生方法主要通过一个名为getNative的内部方法去获取,该方法内部会对传入的参数进行判断,而getNative方法虽然是内部方法,但是在对外暴露的方法实现中很多都借用了该方法去实现,且实现getNative方法借助到的工具函数在很多方法实现中也常常使用,所以单独一篇章讲解getNative以及其相关方法的实现。

获取全局对象

在lodash内部提供了一个root方法可以获取全局对象。

由于在浏览器环境下存在一个self变量会返回一个指向当前 window 对象的引用,所以当环境存在self类型是对象时并且self存在一个Object的值与环境中的Object相同,则返回该self变量作为环境变量root。

同理,由于node环境存在global变量,所以判断global变量,成立的话便返回。

对于兼容性问题,则调用Function('return this')()返回环境变量。

var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

var freeSelf = typeof self == 'object' && self && self.Object === Object && self;

var root = freeGlobal || freeSelf || Function('return this')();

判断当前环境是否有corejs的标记

corejs会在当前环境注入变量标记__core-js_shared__,所以通过判断该变量是否存在即可。

var coreJsData = root['__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);
}

获取一个对象的值

当想要获取一个对象的值时,可以使用getValue方法,该方法仅在lodash内部使用,在方法内部处理了错误数据可能造成的报错问题。

function getValue(object, key) {
  return object == null ? undefined : object[key];
}

获取函数的源代码

获取函数的源代码可以通过toSource方法处理,该方法仅在lodash内部使用,通过调用Function.prototype.toString.call方法获取函数代码片段,如果为null则返回空字符串。

var funcProto = Function.prototype;
var funcToString = funcProto.toString;

function toSource(func) {
  if (func != null) {
    try {
      return funcToString.call(func);
    } catch (e) {}
    try {
      return (func + '');
    } catch (e) {}
  }
  return '';
}

判断是否是原生方法

这里主要用到了前面讲解到的isObject、isMasked、isFunction判断方法。

判断原生方法的原理是由于调用Function.prototype.toString.call之后,如果是原生方法,会带有[native code]字段。

Function.prototype.toString.call(Object.prototype.hasOwnProperty)
// => 'function hasOwnProperty() { [native code] }'

Function.prototype.toString.call(Object.prototype.hasOwnProperty).replace(/[\^$.*+?()[]{}|]/g,'\$&').replace(/hasOwnProperty|(function).*?(?=\()| for .+?(?=\])/g, '$1.*?') + '$'
// => 'function.*?\(\) \{ \[native code\] \}$'

如果参数不等于对象或者是标记的corejs代码则返回false;如果参数是其他类型,则调用正则匹配结果。

其中对于函数类型会用reIsNative 的匹配规则,而其他类型则用reIsHostCtor的匹配规则。

var reRegExpChar = /[\^$.*+?()[]{}|]/g;
var reIsHostCtor = /^[object .+?Constructor]$/;
var funcProto = Function.prototype,
    objectProto = Object.prototype;
var funcToString = funcProto.toString;
var hasOwnProperty = objectProto.hasOwnProperty;

/** 验证是否为本地方法 */
var reIsNative = RegExp('^' +
  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\$&')
  .replace(/hasOwnProperty|(function).*?(?=\()| for .+?(?=\])/g, '$1.*?') + '$'
);

function baseIsNative(value) {
  if (!isObject(value) || isMasked(value)) {
    return false;
  }
  var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
  return pattern.test(toSource(value));
}

获取原生方法

getNative方法可以获取原生方法,通过传递一个对象和一个key,如果对象是原生方法则返回相应的值,不是的话则返回undefined。

这里调用了baseIsNative方法进行判断处理。

function getNative(object, key) {
  var value = getValue(object, key);
  return baseIsNative(value) ? value : undefined;
}

小结

我们通过多个内部方法实现getNative方法,在lodash中大多数方法是存在公用的情况。

实现getNative方法有助于我们在后边实现其他暴露的方法直接调用。