如何解决在 JavaScript 中模仿集合?
如果您在支持 ES6 的环境中编程(例如 node.js,具有您需要的 ES6 功能的特定浏览器或为您的环境转译 ES6
代码),那么您可以使用Set
内置于 ES6 的对象。它具有非常好的功能,可以在您的环境中按原样使用。
对于 ES5 环境中的许多简单的事情,使用 Object
效果很好。如果obj
是您的对象并且A
是一个具有您想要在集合中操作的值的变量,那么您可以执行以下操作:
初始化代码:
// create empty object
var obj = {};
// or create an object with some items already in it
var obj = {"1":true, "2":true, "3":true, "9":true};
在A
列表中:
if (A in obj) {
// put code here
}
从列表中删除“A”(如果存在):
delete obj[A];
如果列表中不存在“A”,则将其添加到列表中
obj[A] = true;
为了完整起见,测试是否A
在列表中会更安全一些:
if (Object.prototype.hasOwnProperty.call(obj, A))
// put code here
}
因为基本对象上的内置方法和/或属性(如constructor
属性)之间的潜在冲突。
的当前工作版本或称为 ES 2015 的东西有一个
。它现在在一些浏览器中实现。由于浏览器可用性随时间而变化,您可以查看此 ES6
兼容性表Set
中的行以查看浏览器可用性的当前状态。
内置 Set 对象的一个优点是它不会像 Object 那样将所有键强制转换为字符串,因此您可以将 5 和 “5” 作为单独的键。而且,您甚至可以直接在集合中使用对象,而无需进行字符串转换。这是一篇文章,描述了 Set 对象的一些功能和MDN 的文档。
我现在已经为 ES6 set 对象编写了一个 polyfill,因此您现在可以开始使用它,如果浏览器支持它,它将自动遵循内置的 set
对象。这样做的好处是您编写的 ES6 兼容代码可以一直工作到 IE7。但是,有一些缺点。ES6 集合接口利用了 ES6
迭代器,因此您可以执行类似的操作for (item of mySet)
,它会自动为您遍历集合。但是,这种语言特性无法通过 polyfill
实现。您仍然可以在不使用新的 ES6 语言特性的情况下迭代 ES6 集合,但坦率地说,如果没有新的语言特性,它不如我在下面包含的其他集合界面那么方便。
在查看两者之后,您可以决定哪一个最适合您。ES6 set polyfill 在这里:https://github.com/jfriend00/ES6-Set 。
仅供参考,在我自己的测试中,我注意到 Firefox v29 Set 实现在当前的规范草案中并不完全是最新的。例如,您不能.add()
像规范描述和我的
polyfill 支持那样链接方法调用。这可能是一个动态规范的问题,因为它尚未最终确定。
如果您想要一个已构建的对象,该对象具有可在任何浏览器中使用的集合上的操作方法,您可以使用一系列不同的预构建对象来实现不同类型的集合。有一个 miniSet,它是实现集合对象基础的小代码。它还有一个功能更丰富的 set 对象和几个派生类,包括 Dictionary(让您存储/检索每个键的值)和 ObjectSet(让您保留一组对象 - JS 对象或 DOM 对象,您可以在其中提供为每个生成唯一密钥的函数,否则 ObjectSet 将为您生成密钥)。
这是 miniSet 的代码副本(最新代码在 github 上)。
"use strict";
//-------------------------------------------
// Simple implementation of a Set in javascript
//
// Supports any element type that can uniquely be identified
// with its string conversion (e.g. toString() operator).
// This includes strings, numbers, dates, etc...
// It does not include objects or arrays though
// one Could implement a toString() operator
// on an object that would uniquely identify
// the object.
//
// Uses a javascript object to hold the Set
//
// This is a subset of the Set object designed to be smaller and faster, but
// not as extensible. This implementation should not be mixed with the Set object
// as in don't pass a miniSet to a Set constructor or vice versa. Both can exist and be
// used separately in the same project, though if you want the features of the other
// sets, then you should probably just include them and not include miniSet as it's
// really designed for someone who just wants the smallest amount of code to get
// a Set interface.
//
// s.add(key) // adds a key to the Set (if it doesn't already exist)
// s.add(key1, key2, key3) // adds multiple keys
// s.add([key1, key2, key3]) // adds multiple keys
// s.add(otherSet) // adds another Set to this Set
// s.add(arrayLikeObject) // adds anything that a subclass returns true on _isPseudoArray()
// s.remove(key) // removes a key from the Set
// s.remove(["a", "b"]); // removes all keys in the passed in array
// s.remove("a", "b", ["first", "second"]); // removes all keys specified
// s.has(key) // returns true/false if key exists in the Set
// s.isEmpty() // returns true/false for whether Set is empty
// s.keys() // returns an array of keys in the Set
// s.clear() // clears all data from the Set
// s.each(fn) // iterate over all items in the Set (return this for method chaining)
//
// All methods return the object for use in chaining except when the point
// of the method is to return a specific value (such as .keys() or .isEmpty())
//-------------------------------------------
// polyfill for Array.isArray
if(!Array.isArray) {
Array.isArray = function (vArg) {
return Object.prototype.toString.call(vArg) === "[object Array]";
};
}
function MiniSet(initialData) {
// Usage:
// new MiniSet()
// new MiniSet(1,2,3,4,5)
// new MiniSet(["1", "2", "3", "4", "5"])
// new MiniSet(otherSet)
// new MiniSet(otherSet1, otherSet2, ...)
this.data = {};
this.add.apply(this, arguments);
}
MiniSet.prototype = {
// usage:
// add(key)
// add([key1, key2, key3])
// add(otherSet)
// add(key1, [key2, key3, key4], otherSet)
// add supports the EXACT same arguments as the constructor
add: function() {
var key;
for (var i = 0; i < arguments.length; i++) {
key = arguments[i];
if (Array.isArray(key)) {
for (var j = 0; j < key.length; j++) {
this.data[key[j]] = key[j];
}
} else if (key instanceof MiniSet) {
var self = this;
key.each(function(val, key) {
self.data[key] = val;
});
} else {
// just a key, so add it
this.data[key] = key;
}
}
return this;
},
// private: to remove a single item
// does not have all the argument flexibility that remove does
_removeItem: function(key) {
delete this.data[key];
},
// usage:
// remove(key)
// remove(key1, key2, key3)
// remove([key1, key2, key3])
remove: function(key) {
// can be one or more args
// each arg can be a string key or an array of string keys
var item;
for (var j = 0; j < arguments.length; j++) {
item = arguments[j];
if (Array.isArray(item)) {
// must be an array of keys
for (var i = 0; i < item.length; i++) {
this._removeItem(item[i]);
}
} else {
this._removeItem(item);
}
}
return this;
},
// returns true/false on whether the key exists
has: function(key) {
return Object.prototype.hasOwnProperty.call(this.data, key);
},
// tells you if the Set is empty or not
isEmpty: function() {
for (var key in this.data) {
if (this.has(key)) {
return false;
}
}
return true;
},
// returns an array of all keys in the Set
// returns the original key (not the string converted form)
keys: function() {
var results = [];
this.each(function(data) {
results.push(data);
});
return results;
},
// clears the Set
clear: function() {
this.data = {};
return this;
},
// iterate over all elements in the Set until callback returns false
// myCallback(key) is the callback form
// If the callback returns false, then the iteration is stopped
// returns the Set to allow method chaining
each: function(fn) {
this.eachReturn(fn);
return this;
},
// iterate all elements until callback returns false
// myCallback(key) is the callback form
// returns false if iteration was stopped
// returns true if iteration completed
eachReturn: function(fn) {
for (var key in this.data) {
if (this.has(key)) {
if (fn.call(this, this.data[key], key) === false) {
return false;
}
}
}
return true;
}
};
MiniSet.prototype.constructor = MiniSet;
解决方法
我正在使用 JavaScript。我想存储一个 唯一的 、无序的字符串值列表,具有以下属性:
- 一种快速询问“列表中是否有 A”的方法?
- 一种快速的方法来执行“如果列表中存在 A,则从列表中删除它”
- 一种快速的方法来执行“如果 A 不存在,则将 A 添加到列表中”。
我真正想要的是一套。关于在 JavaScript 中模仿集合的最佳方法有什么建议吗?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。