Javascript 如何可靠地检查对象是否为EcmaScript 6映射/集?

Javascript 如何可靠地检查对象是否为EcmaScript 6映射/集?,javascript,dictionary,collections,set,ecmascript-6,Javascript,Dictionary,Collections,Set,Ecmascript 6,我只想检查对象是否是映射或集合,而不是数组 function myFunc(arg) { if (_.isArray(arg)) { // doSomethingWithArray(arg) } if (isMap(arg)) { // doSomethingWithMap(arg) } if (isSet(arg)) { // doSomethingWithSet(arg) } } 要检查数组,我正在使用lodash的\uu.isArray

我只想检查对象是否是
映射
集合
,而不是
数组

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

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

  if (isSet(arg)) {
    // doSomethingWithSet(arg)
  }
}
要检查数组,我正在使用lodash的
\uu.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
操作符返回
true


编辑-虽然
instanceof
在大多数情况下都能正常工作,但正如Bergi的回答所述,在某些情况下它不能正常工作。

这种情况类似于ES5之前正确可靠地检测阵列的方法。有关实现
isArray
的可能陷阱,请参阅

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

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

  if (isSet(arg)) {
    // doSomethingWithSet(arg)
  }
}
我们可以使用

  • obj.constructor==Map
    /
    Set
    ,但这对子类实例不起作用(并且很容易被欺骗)
  • obj instanceof Map
    /
    Set
    ,但这仍然不能跨领域工作(并且可能会被原型损坏所欺骗)
  • obj[Symbol.toStringTag]=“Map”
    /
    “Set”
    ,但这很容易再次被欺骗
为了真正确定,我们需要测试对象是否有
[[MapData]]
/
[[SetData]]]
内部插槽。这是不容易访问-它的内部。不过,我们可以使用黑客:

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
——它简单易懂,性能优良,适用于大多数合理的情况。或者,您立即进行duck键入,只检查对象是否具有
has
/
get
/
set
/
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]';
}

除非该方法的原型被覆盖

否则,即使该集合是
集合
的子类,该方法是否仍有效?我应该说明的是,这是一个值得关注的问题。@nackjicholson这取决于您如何创建子类。如果构造函数的
.prototype
位于对象的prototype链中,则
instanceof
运算符返回
true
。@nackjicholson:只要您执行
类MySet extensed Set
,就可以。请注意,如果您正在为将重新分发的库编写代码,最好先检查是否定义了
window.Set
(除非您的库中包含适用于旧浏览器的垫片)。(或者,为了使它也能在服务器上工作,您可以检查设置的
类型!=“undefined”
)。是的。事实上,这种情况更糟糕,因为与数组不同,集合只在最新的规范中使用。在较旧的浏览器中使用垫片是很常见的,我刚刚意识到
Object.prototype.toString
检查对这些浏览器不起作用。我可能会在这里发布一个最终的答案。为什么不使用
Object.prototype.toString.call(o)='[objectset]'
?除了有人重写
Object.prototype.toString
之外,还有其他情况会失败吗?(我不认为重写的
Object.prototype.toString
值得担心-这是一种会破坏许多现有库的糟糕做法。)@MattBrowne
Object.prototype.toString
相当于使用
符号.toString
方法-您不需要重写全局,它可以在每个对象的基础上进行处理。是否严格需要布尔检查?似乎可以在没有的情况下使用falsy值。是的,这是必需的,因为在项上调用object.toString,如果它未定义或为null,它将在Chrome和Firefox中为
对象.prototype.toString.call(null)
调用
对象时抛出错误。