资讯专栏INFORMATION COLUMN

JavaScript中真正的哈希映射(译)

DTeam / 2606人阅读

摘要:然而,对象字面量不是真正意义上的哈希映射,如果使用不当可能会构成潜在的隐患。空对象创建一个真正的哈希映射的秘诀就是避免原型,及其带来的包袱。在此之前,甚至之后,你应该使用空对象满足你所有的基本哈希映射需求。

  

在JavaScript中存储键值对的一个简单常见的方法是使用对象字面量。然而,对象字面量不是真正意义上的哈希映射,如果使用不当可能会构成潜在的隐患。虽然目前JavaScript可能没有提供原生的hashmap(至少不能跨浏览器),对象字面量如果没有隐患就能达到所需的功能也许是一个更好的选择。

对象字面量存在的问题

对象字面的问题在于其原型链继承自Object原型上的对象和方法会破坏其维持键值的机制。以toString方法为例,使用in操作符检查同名属性会导致错误的结果:

javascriptvar map = {};
`toString` in map; // true

上面的错误之所以会发生,是因为in操作符会从对象的原型链上查找继承属性。为了解决该问题,我们可以用hasOwnProperty方法来确定键值的存在性,因为该方法只检查对象本身的属性:

javascriptvar map = {};
map.hasOwnProperty("toString"); // false

上面的方法能够良好的工作,除非你遇到一个名为hasOwnProperty键。重写此方法将会因为尝试调用hasOwnProperty方法而导致意外的行为,根据新的值最有可能导致错误:

javascriptvar map = {};
map.hasOwnProperty = "foo";
map.hasOwnProperty("hasOwnProperty"); // TypeError

一个快速的修正方法是利用一个通用且没有被篡改的对象字面量,并在你指定的hashmap上下文中执行hasOwnProperty方法:

javascriptvar map = {};
map.hasOwnProperty = "foo";
{}.hasOwnProperty.call(map, "hasOwnProperty"); // true

尽管实际工作时没有任何问题,但对象字面量还是限制了它的使用。举个例子,每次你在for...in循环里面遍历一个对象的属性,你都要过滤其原型链中的属性:

javascriptvar map = {};
var has = {}.hasOwnProperty;

for (var key in map) {
    if(has.call(map, key)) {
        // ...
    }
}

一段时间后,可能会变得有点乏味。值得庆幸的是,有一个更好的办法。

空对象

创建一个真正的哈希映射的秘诀就是避免原型,及其带来的包袱。我们可以利用ES5中引入的Object.create方法达到该目的。该方法的特别之处在于你可以给一个新对象明确定义原型。举个例子,用一个较复杂的方式定义一个简单对象字面量:

javascriptvar obj = {};
// 等价于
var obj = Object.create(Object.prototype);

除了能够定义一个你选择的原型,你也能够通过传入一个null放弃传入原型:

javascriptvar map = Object.create(null);

map instanceof Object; // false
Object.prototype.isPrototypeOf(map); // false
Object.getPrototypeOf(map); // null

这些空对象对于哈希映射是理想的,因为缺少[[Prototype]]避免了命名冲突。由于该对象完全是空的,它会抵制任何形式的强制转换,试图这样做将导致一个错误:

javascriptvar map = Object.create(null);
map + ""; // TypeError: Cannot convert object to primitive value

空对象没有任何初始值或者字符串表现形式,因为空对象除了作为键值对的存储空间没有为任何其他事情做打算,简单又普通。

注意hasOwnProperty方法在空对象中也消失了,这无关紧要,因为in操作符可以无异常的工作了:

javascriptvar map = Object.create(null);
"toString" in map; // false

更好的是,乏味的for...in循环变得更加简单。我们最终可以按其本身的意思写一个循环:

javascriptvar map = Object.create(null);
for (var key in map) {
    // ...
}

虽然存在差异,但对所有的意图和目的,它仍然表现得就像一个对象字面量。属性可以利用.或则[]访问,对象可以被序列化,且对象仍然可以使用上下文对象的原型方法:

javascriptvar 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

甚至不同检查类型的方法将会告诉你从对象字面中期望得到什么:

javascriptvar map = Object.create(null);
typeof map; // object
{}.toString.call(map); // [object Object]
{}.valueOf.call(map); // Object {}

这一切使得空对象代替对象字面量变得简单,让他们很好地集成到一个现有的应用程序,而不会引起大范围的变化。

总结

在简单的键值存储的背景下,使用空对象是对象字面量的有效替代方案,用明确的定义消除对象字面量的怪癖。对于更全面的数据结构,ES6将以MapSet形式引入原生的hashmap。在此之前,甚至之后,你应该使用空对象满足你所有的基本哈希映射需求。

欢迎光临我的个人博客:风影博客

参考

原文地址:True Hash Maps in JavaScript

Object.create()

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92325.html

相关文章

  • [] 理解数组在 PHP 内部实现(给PHP开发者PHP源码-第四部分)

    摘要:为了防止你错过了之前的文章,以下是链接第一部分给开发者的源码源码结构第二部分理解内部函数的定义第三部分的变量实现所有的东西都是哈希表基本上,里面的所有东西都是哈希表。哈希后的结果可以被作为正常的数组的键值又名为内存块。表示哈希表的容量。 文章来自:http://www.hoohack.me/2016/02/15/understanding-phps-internal-array-im...

    solocoder 评论0 收藏0
  • JavaScript 框架探索与变迁(上)

    摘要:正文在年,框架的选择并不少。特别的,通过思考这些框架分别如何处理状态变化是很有用的。本文探索以下的数据绑定,的脏检查的虚拟以及它与不可变数据结构之间的联系。当状态产生变化时,只有真正需要更新的部分才会发生改变。 译者言 近几年可谓是 JavaScript 的大爆炸纪元,各种框架类库层出不穷,它们给前端带来一个又一个的新思想。从以前我们用的 jQuery 直接操作 DOM,到 Backb...

    Jaden 评论0 收藏0
  • []如何写一个webpack插件

    摘要:原文译者插件能够将引擎的全部潜力暴露给第三方的开发者。当将一个插件应用到环境中,这个插件将会获得一个对于这个的引用。表示有关模块资源,已编译资源,已更改文件和监视依赖关系的当前状态的信息。 原文:how to write a plugin 译者:neal1991 welcome to star my articles-translator , providing you advanc...

    wupengyu 评论0 收藏0
  • []如何基于Laravel构建Vue应用(一)

    摘要:使用能优雅的构建并且与单页面应用程序完美结合。我们将重点关注所需的所有部分,然后在后续教程中,我们将进一步演示如何使用作为层。例如,如果用户刷新路由,我们将需要匹配该路由并返回应用程序模板。运行应用程序该基础用于构建具有和路由器的。 使用Laravel能优雅的构建API并且与Vue单页面应用程序(SPA)完美结合。在本教程中,我们将展示如何启动和运行Vue路由器以及用于构建SPA的La...

    Rocko 评论0 收藏0
  • [] JavaScript 性能优化杀手

    摘要:原文引言这篇文档包含了如何避免使代码性能远低于预期的建议尤其是一些会导致牵涉到等无法优化相关函数的问题一些背景在中并没有解释器但却有两个不同的编译器通用编译器和优化编译器这意味着你的代码总是会被编译为机器码后直接运行这样一定很快咯并不是 原文:http://dev.zm1v1.com/2015/08/19/javascript-optimization-killers/引言 这篇文档包...

    MockingBird 评论0 收藏0

发表评论

0条评论

DTeam

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<