如何可靠地检查对象是 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
你可以找到我在这里引用的代码。
我只想检查对象是 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
.
我们可以使用
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
你可以找到我在这里引用的代码。