如何可靠地检查对象是 EcmaScript 6 Map/Set?

How to reliably check an object is an EcmaScript 6 Map/Set?

我只想检查对象是 Map 还是 Set 而不是 Array

检查我正在使用 lodash 的数组 _.isArray

function myFunc(arg) {
  if (_.isArray(arg)) {
    // doSomethingWithArray(arg)
  }

  if (isMap(arg)) {
    // doSomethingWithMap(arg)
  }

  if (isSet(arg)) {
    // doSomethingWithSet(arg)
  }
}

如果我要实施 isMap/isSet,它需要看起来像什么?如果可能的话,我希望它也能够捕获 Map/Set 的子类。

您可以使用 instanceof 运算符:

function isSet(candidate) {
  return candidate instanceof Set;
}

如果候选对象在其原型链中有Set.prototype,则instanceof运算符returnstrue.

edit — 虽然 instanceof 的东西会在 大多数 的时候起作用,但在某些情况下它不会' t,如 Bergi 的回答所述。

这种情况类似于正确可靠地检测数组的 ES5 之前的方法。有关实施 isArray.

的可能陷阱,请参阅 this great article

我们可以使用

  • obj.constructor == Map/Set,但这对子类实例不起作用(并且很容易被欺骗)
  • obj instanceof Map/Set,但这仍然无法跨领域工作(并且可能被原型修改所欺骗)
  • obj[Symbol.toStringTag] == "Map"/"Set",不过那又可以轻而易举的被骗了。

确实如此,我们需要测试对象是否具有 [[MapData]]/[[SetData]] 内部插槽。这不是那么容易访问 - 它是内部的。不过,我们可以使用 hack:

function isMap(o) {
    try {
        Map.prototype.has.call(o); // throws if o is not an object or has no [[MapData]]
        return true;
    } catch(e) {
        return false;
    }
}
function isSet(o) {
    try {
        Set.prototype.has.call(o); // throws if o is not an object or has no [[SetData]]
        return true;
    } catch(e) {
        return false;
    }
}

对于一般用途,我推荐 instanceof - 它简单、易懂、高效,适用于大多数合理的情况。或者你马上去鸭子打字,只检查对象是否有 has/get/set/delete/add/delete方法。

您可以简单地使用:

export function isMap(item) {
  return !!item && Object.prototype.toString.call(item) === '[object Map]';
}

export function isSet(item) {
  return !!item && Object.prototype.toString.call(item) === '[object Set]';
}

除非这个方法的原型被覆盖

Vue源码中使用了以下方法。你可以从这里借用这个想法。

export const isMap = (val: unknown): val is Map<any, any> =>
  toTypeString(val) === '[object Map]'
export const isSet = (val: unknown): val is Set<any> =>
  toTypeString(val) === '[object Set]'


export const objectToString = Object.prototype.toString
export const toTypeString = (value: unknown): string =>
  objectToString.call(value)

很容易理解。将对象转换为字符串后。然后你只需要检查字符串的值是 '[object Map]' 还是 '[object Set]'

我强烈建议前端阅读源代码中的实用函数。您总能找到最佳实践。

这里是 link 到 Vue 的源代码:https://github.com/vuejs/vue-next/blob/master/packages/shared/src/index.ts

你可以找到我在这里引用的代码。