关于lodash内部方法getTag的实现

337 阅读4分钟

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

前言

lodash里的getTag作为内部的一个方法,在很多方法的实现中常常用到,该方法主要是获取目标的标识来判断是哪种类型的数据。

在实现这个方法之前,需要借助一系列判断数据类型的方法,我们先来看看其中借助到的判断方法。

DataView

DataView视图是一个可以从二进制ArrayBuffer对象中读写多种数值类型的底层接口,使用它时,不用考虑不同平台的字节序问题。

new DataView(new ArrayBuffer(16))
// => DataView(16)

typeof new DataView(new ArrayBuffer(16))
// => 'object'

对于DataView的数据如果单纯用typeof操作符很难和普通对象区分开来。

在lodash内部获取DataView方法通过getNative去获取,通过传递root参数获取根元素,如果当前环境存在DataView构造函数则赋值给DataView 变量,并导出使用。(getNative方法 和root变量的实现在之前的篇章已经讲解了)

下面为源码实现:

import getNative from './_getNative.js';
import root from './_root.js';

/* Built-in method references that are verified to be native. */
var DataView = getNative(root, 'DataView');

Symbol

获取Symbol构造函数可以通过root 变量去获取:

var Symbol = root.Symbol;

Map

同样的,获取Map构造函数通过getNative 方法和root 变量去获取:

import getNative from './_getNative.js';
import root from './_root.js';

var Map = getNative(root, 'Map');

Set

借助getNative 方法和root 变量去获取Set构造函数:

import getNative from './_getNative.js';
import root from './_root.js';

var Set = getNative(root, 'Set');

export default Set;

Promise

借助getNative 方法和root 变量去获取Promise构造函数:

import getNative from './_getNative.js';
import root from './_root.js';

var Promise = getNative(root, 'Promise');

WeakMap

借助getNative 方法和root 变量去获取WeakMap 构造函数:

import getNative from './_getNative.js';
import root from './_root.js';

var WeakMap = getNative(root, 'WeakMap');

objectToString

objeToString方法主要是返回Object.prototype.toString,使得调用更加方便点。

var objectProto = Object.prototype;
var nativeObjectToString = objectProto.toString;

function objectToString(value) {
  return nativeObjectToString.call(value);
}

getRawTag

getRawTag作为lodash的内部方法,同样是获取字符串化的对象标志,是getTag的基础方法,主要是通过Object.prototype.toString.call方法去获取字符串化标志,而getTag则是在这之上做了兼容性判断处理和增强。(这里symToStringTag的获取借助前面封装的获取Symbol 构造函数的方法,如果本地不存在Symbol 构造函数则Symbol 的字符串化标志位undefined,存在的话调用身上的toString方法)

该方法实现如下:

import Symbol from './_Symbol.js';

var objectProto = Object.prototype;
var hasOwnProperty = objectProto.hasOwnProperty;
var nativeObjectToString = objectProto.toString;
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;

function getRawTag(value) {
  var isOwn = hasOwnProperty.call(value, symToStringTag),
      tag = value[symToStringTag];

  try {
    value[symToStringTag] = undefined;
    var unmasked = true;
  } catch (e) {}

  var result = nativeObjectToString.call(value);
  if (unmasked) {
    if (isOwn) {
      value[symToStringTag] = tag;
    } else {
      delete value[symToStringTag];
    }
  }
  return result;
}

baseGetTag

baseGetTag是同样是getTag的基本实现,是getRawTag的增强,这里实现借助前面的getRawTag 方法,针对null返回[object Null]标志,针对Symbol则调用getRawTag方法,其余情况则调用objectToString方法。

import Symbol from './_Symbol.js';
import getRawTag from './_getRawTag.js';
import objectToString from './_objectToString.js';

var nullTag = '[object Null]',
    undefinedTag = '[object Undefined]';

var symToStringTag = Symbol ? Symbol.toStringTag : undefined;

function baseGetTag(value) {
  if (value == null) {
    return value === undefined ? undefinedTag : nullTag;
  }
  return (symToStringTag && symToStringTag in Object(value))
    ? getRawTag(value)
    : objectToString(value);
}

export default baseGetTag;

getTag

上面的构造函数获取都是通过getNative 方法和root 变量,在实现getTag方法我们还需要借助之前篇章中讲解到的baseGetTag和toSource,toSource 方法主要是获取函数内部的源代码。

getTag结合之前的方法,使用switch进行匹配,当匹配到特定标志时返回特定标签。

下面为代码实现:

import DataView from './_DataView.js';
import Map from './_Map.js';
import Promise from './_Promise.js';
import Set from './_Set.js';
import WeakMap from './_WeakMap.js';
import baseGetTag from './_baseGetTag.js';
import toSource from './_toSource.js';

var mapTag = '[object Map]',
    objectTag = '[object Object]',
    promiseTag = '[object Promise]',
    setTag = '[object Set]',
    weakMapTag = '[object WeakMap]';

var dataViewTag = '[object DataView]';

var dataViewCtorString = toSource(DataView),
    mapCtorString = toSource(Map),
    promiseCtorString = toSource(Promise),
    setCtorString = toSource(Set),
    weakMapCtorString = toSource(WeakMap);

var getTag = baseGetTag;

// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
    (Map && getTag(new Map) != mapTag) ||
    (Promise && getTag(Promise.resolve()) != promiseTag) ||
    (Set && getTag(new Set) != setTag) ||
    (WeakMap && getTag(new WeakMap) != weakMapTag)) {
  getTag = function(value) {
    var result = baseGetTag(value),
        Ctor = result == objectTag ? value.constructor : undefined,
        ctorString = Ctor ? toSource(Ctor) : '';

    if (ctorString) {
      switch (ctorString) {
        case dataViewCtorString: return dataViewTag;
        case mapCtorString: return mapTag;
        case promiseCtorString: return promiseTag;
        case setCtorString: return setTag;
        case weakMapCtorString: return weakMapTag;
      }
    }
    return result;
  };
}

小结

getTag的实现依托内部封装的各个方法,其功能增强也是一步一步演进而非一蹴而成。

后续在实现方法时候我们可以直接借助这些封装的工具方法直接调用进行处理。