我正在使用JavaScript。我想存储具有以下属性的 唯一 ,无序字符串值的列表:
我真正想要的是一套。有什么建议以最佳方式模仿JavaScript中的集合吗?
这个问题建议使用Object,其键存储属性,并且所有值都设置为true:这是明智的方法吗?
如果要在支持ES6的环境中编程(例如,node.js,具有ES6功能的特定浏览器或为环境转换ES6代码),则可以使用SetES6中内置的对象。它具有非常好的功能,可以在您的环境中直接使用。
Set
对于ES5环境中的许多简单事物,使用Object效果很好。如果obj是您的对象,并且A是一个具有要在集合中操作的值的变量,则可以执行以下操作:
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};
问题1: 是A在列表中:
if (A in obj) { // put code here }
问题2: 如果存在,请从列表中删除“ A”:
delete obj[A];
问题3: 如果尚未在列表中添加“ A”
obj[A] = true;
为了完整起见,使用以下方法测试是否A在列表中会更加安全:
if (Object.prototype.hasOwnProperty.call(obj, A)) // put code here }
因为基础对象上的内置方法和/或属性(例如constructor属性)之间可能存在冲突。
constructor
ES6上的侧栏: ECMAScript 6 或ES 2015 的当前工作版本具有 内置的Set对象 。现在已在某些浏览器中实现。由于浏览器的可用性随时间变化的,你可以看看线Set在此ES6兼容性表,查看浏览器可用性的当前状态。
内置Set对象的一个优点是,它不像Object那样将所有键都强制转换为字符串,因此您可以将5和“ 5”作为单独的键。而且,您甚至可以直接在集合中使用对象,而无需进行字符串转换。
我现在为ES6设置对象编写了一个polyfill,因此您现在就可以开始使用它,如果浏览器支持,它将自动遵从内置设置对象。这样做的好处是您正在编写与ES6兼容的代码,这些代码将一直工作到IE7。但是,还有一些缺点。ES6 set接口利用了ES6迭代器,因此您可以做类似的事情for (item of mySet),它将为您自动遍历set6。但是,这种语言功能无法通过polyfill实现。您仍然可以在不使用新ES6语言功能的情况下迭代ES6集,但是坦率地说,在没有新语言功能的情况下,它不如我在下面包括的其他集界面那样方便。
for (item of mySet)
查看两者后,您可以决定哪一个最适合您。
仅供参考,在我自己的测试中,我注意到Firefox v29 Set实施不是最新的规范草案。例如,您不能.add()像规范说明和我的polyfill支持那样链接方法调用。这可能是运动规范的问题,因为它尚未最终确定。
.add()
预先构建的Set对象: 如果想要一个已经构建的对象,该对象具有可在任何浏览器中使用的对集合进行操作的方法,则可以使用实现不同类型集合的一系列不同的预先构建的对象。有一个miniSet,它是一些小代码,可实现set对象的基础。它还具有功能更丰富的set对象和几个派生对象,包括Dictionary(让您为每个键存储/检索一个值)和ObjectSet(让您保留一组对象- JS对象或DOM对象,您可以在其中提供为每个键生成唯一键的函数,否则ObjectSet会为您生成键)。
)。
"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;