在JavaScript中存储键值对的一个简朴罕见的要领是运用对象字面量。但是,对象字面量不是真正意义上的哈希映照,假如运用不当能够会组成潜伏的隐患。虽然现在JavaScript能够没有供应原生的hashmap(最少不能跨浏览器),对象字面量假如没有隐患就能够到达所需的功用也许是一个更好的挑选。
对象字面量存在的题目
对象字面的题目在于其原型链继续自Object
原型上的对象和要领会损坏其保持键值的机制。以toString
要领为例,运用in
操作符搜检同名属性会致使毛病的效果:
javascript
var map = {}; `toString` in map; // true
上面的毛病之所以会发作,是因为in
操作符会从对象的原型链上查找继续属性。为了处理该题目,我们能够用hasOwnProperty
要领来肯定键值的存在性,因为该要领只搜检对象自身的属性:
javascript
var map = {}; map.hasOwnProperty('toString'); // false
上面的要领能够优越的事变,除非你碰到一个名为hasOwnProperty
键。重写此要领将会因为尝试挪用hasOwnProperty
要领而致使不测的行动,依据新的值最有能够致使毛病:
javascript
var map = {}; map.hasOwnProperty = 'foo'; map.hasOwnProperty('hasOwnProperty'); // TypeError
一个疾速的修改要领是应用一个通用且没有被改动的对象字面量,并在你指定的hashmap
高低文中实行hasOwnProperty
要领:
javascript
var map = {}; map.hasOwnProperty = 'foo'; {}.hasOwnProperty.call(map, 'hasOwnProperty'); // true
只管实际事变时没有任何题目,但对象字面量照样限定了它的运用。举个例子,每次你在for...in
轮回内里遍历一个对象的属性,你都要过滤其原型链中的属性:
javascript
var map = {}; var has = {}.hasOwnProperty; for (var key in map) { if(has.call(map, key)) { // ... } }
一段时间后,能够会变得有点乏味。值得光荣的是,有一个更好的方法。
空对象
建立一个真正的哈希映照的窍门就是防止原型,及其带来的包袱。我们能够应用ES5
中引入的Object.create
要领到达该目标。该要领的特别之处在于你能够给一个新对象明肯定义原型。举个例子,用一个较庞杂的体式格局定义一个简朴对象字面量:
javascript
var obj = {}; // 等价于 var obj = Object.create(Object.prototype);
除了能够定义一个你挑选的原型,你也能够经由过程传入一个null
摒弃传入原型:
javascript
var map = Object.create(null); map instanceof Object; // false Object.prototype.isPrototypeOf(map); // false Object.getPrototypeOf(map); // null
这些空对象关于哈希映照是抱负的,因为缺乏[[Prototype]]
防止了定名争执。因为该对象完全是空的,它会抵抗任何情势的强迫转换,试图如许做将致使一个毛病:
javascript
var map = Object.create(null); map + ""; // TypeError: Cannot convert object to primitive value
空对象没有任何初始值或许字符串表现情势,因为空对象除了作为键值对的存储空间没有为任何其他事变做盘算,简朴又一般。
注重hasOwnProperty
要领在空对象中也消逝了,这可有可无,因为in
操作符能够无非常的事变了:
javascript
var map = Object.create(null); 'toString' in map; // false
更好的是,乏味的for...in
轮回变得越发简朴。我们终究能够按其自身的意义写一个轮回:
javascript
var map = Object.create(null); for (var key in map) { // ... }
虽然存在差别,但对一切的企图和目标,它依然表现得就像一个对象字面量。属性能够应用.
或则[]
接见,对象能够被序列化,且对象依然能够运用高低文对象的原型要领:
javascript
var map = Object.create(null); Object.defineProperties(map, { 'foo': { value: 1, enumerable: true }, 'bar': { value: 2, enumerable: false } }); map.foo; // 1 map['bar']; // 2 JSON.stringify(map); // {"foo": 1} {}.hasOwnProperty.call(map, 'foo'); // true {}.propertyIsEnumerable.call(map, 'bar'); // false
以至差别搜检范例的要领将会通知你从对象字面中希冀获得什么:
javascript
var map = Object.create(null); typeof map; // object {}.toString.call(map); // [object Object] {}.valueOf.call(map); // Object {}
这一切使得空对象替换对象字面量变得简朴,让他们很好地集成到一个现有的应用程序,而不会引发大范围的变化。
总结
在简朴的键值存储的背景下,运用空对象是对象字面量的有用替换计划,用明白的定义消弭对象字面量的怪癖。关于更全面的数据结构,ES6
将以Map
和Set
情势引入原生的hashmap
。在此之前,以至以后,你应当运用空对象满足你一切的基础哈希映照需求。
欢迎光临我的个人博客:风影博客