[Effective JavaScript 笔记]第43条:使用Object的直接实例构造轻量级的字典
js对象的核心是一个字符串属性名与属性值的映射表。使用对象实现字典易如反掌,字典是可变长的字符串与值的映射集合。
for...in
js提供了枚举一个对象属性名的利器--for...in循环。
var dict={zhangsan:34,lisi:24,wangwu:62};
var people=[];
for(var name in dict){
people.push(name+":"+dict[name]);
}
people;//["zhangsan:34", "lisi:24", "wangwu:62"]
使用自定义对象
但一些自定义的对象还会继承其原型对象中的属性,for...in循环除了枚举"自身"属性外,原型链中的属性也不会放过。下面看一下例子
function NaiveDict(){
}
NaiveDict.prototype.count=function(){
var i=0;
for(var name in this){
i++;
}
return i;
};
NaiveDict.prototype.toString=function(){
return '[Object NaiveDict]';
};
var dict=new NaiveDict();
dict.zhangsan=24;
dict.lisi=34;
dict.wangwu=62;
dict.count();//5
上面的代码,实例化一个dict对象,并添加了私有属性(zhangsan,lisi,wangwu)并继承了原型对象的方法(toString,count),所以当调用count方法时,这个时候的for...in循环,不仅枚举了了私有属性也枚举了原型对象的方法属性(zhangsan,lisi,wangwu,toString,count)。
使用数组类型
利用js的灵活性,我们可以给任意的类型的对象添加属性,因此给数组添加属性似乎能工作。
var dict=new Array();
dict.zhangsan=24;
dict.lisi=34;
dict.wangwu=62;
dict.zhangsang;//24
当我们给数组的原型对象添加一些原型方法,也就是前面讲的猴子补丁ployfill的使用时,这个时候我们再枚举上面的dict对象的属性时,错误就出现了。
Array.prototype.first=function(){
return this[0];
};
Array.prototype.last=function(){
return this[this.length-1];
};
Array.prototype.eq=function(idx){
if(idx<0)idx=this.length+idx;
return this[idx];
};
运行一下枚举
var names=[];
for(var name in dict){
names.push(name);
}
names;//["zhangsan", "lisi", "wangwu","first","last"]
轻量级字典的正确姿势
首要原则
应该仅仅将Object的直接实例作为字典,而不是子类,或其它对象。
上面的数组的例子可以改成下面这些的代码
var dict={};
dict.zhangsan=24;
dict.lisi=34;
dict.wangwu=62;
dict.zhangsang;
var names=[];
for(var name in dict){
names.push(name);
}
names;//["zhangsan", "lisi", "wangwu"]
需要注意
还是无法避免对于Object.prototype对象的修改,但可以将风险仅仅局限在Object.prototype。
优点
虽说所有人都可以修改Object.prototype对象,也会对for...in循环造成影响。但相比之下,增加属性到Array.prototype中是合理的。如之前给不支持数组标准方法的环境中将这些方法增加到Array.prototype中。这些属性也会导致for...in循环,坚持Object的直接实例原则,可以使得for...in循环摆脱原型污染的影响。对于构建行为正确的字典,这是个必要非充分条件。
提示
使用对象字面量构建轻量级字典
轻量级字典应该是Object.prototype的直接子类,以使for...in循环免受原型的污染
附录:for...in
以下内容来自:Mozilla 开发者社区
以任意序迭代一个对象的可枚举属性。每个不同的属性,语句都会被执行一次。
语法
for(variable in object){...}
参数
variable:每次迭代,一个不同的属性名将赋予variable
object:可枚举属性被迭代的对象
描述
只言遍历可枚举属性。
循环将迭代对象的所有可枚举属性和从它的构造函数的prototype继承而来的
删除、添加或修改属性
for...in循环以任意序迭代一个对象的属性。如果一个属性在一次迭代中被修改,在稍后被访问,其在循环中的值是其在稍后时间的值。一个在被访问之前已经被删除的属性将不会在之后被访问。在迭代进行时被添加到对象的属性,可能在之后的迭代被访问,也可能被忽略。通常,在迭代过程中最好不要在对象上进行添加、修改或删除属性的操作,除非是对当前正在被访问的属性。因为访问的无序性,都无法保证,添加、修改或删除的属性,会在之后被访问。
Array迭代和for...in
数组索引是可枚举的整数名,其他方面和普通对象属性没有区别。for...in的无序性,无法保证枚举是按索引顺序进行,但会返回所有可枚举的属性,包括非整数名称和继承的属性。
如果次序很重要,可以使用for循环来迭代数组。
仅迭代自身的属性
只需要对象本身的属性,不包括它继承来的。可以使用getOwnPropertyNames()或执行hasOwnProperty()来确定某属性是否是对象自身的。
例子
返回一个对象的所有可枚举属性
var obj={a:2,b:1,c:30};
for(var prop in obj){
console.log('obj.'+prop+'='+obj[prop]);
}
// "obj.a = 2"// "obj.b = 1"// "obj.c = 30"
只返回对象自身的可枚举属性
var triangle = {a:1, b:2, c:3};
function ColoredTriangle() {
this.color = "red";
}
ColoredTriangle.prototype = triangle;
var obj = new ColoredTriangle();
for (var prop in obj) {
if( obj.hasOwnProperty( prop ) ) {
console.log("o." + prop + " = " + obj[prop]);
}
}
// "o.color = red"
[Effective JavaScript 笔记]第43条:使用Object的直接实例构造轻量级的字典的更多相关文章
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染
之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...
- [Effective JavaScript 笔记]第44条:使用null原型以防止原型污染
第43条中讲到的就算是用了Object的直接实例,也无法完全避免,Object.prototype对象修改,造成的原型污染.防止原型污染最简单的方式之一就是不使用原型.在ES5之前,并没有标准的方式创 ...
- [Effective JavaScript 笔记]第38条:在子类的构造函数中调用父类的构造函数
示例 场景类 场景图(scene)是在可视化的过程中(如游戏或图形仿真场景)描述一个场景的对象集合.一个简单的场景包含了在该场景中的所有对象(称角色),以及所有角色的预加载图像数据集,还包含一个底层图 ...
- [Effective JavaScript 笔记]第47条:绝不要在Object.prototype中增加可枚举的属性
之前的几条都不断地重复着for...in循环,它便利好用,但又容易被原型污染.for...in循环最常见的用法是枚举字典中的元素.这里就是从侧面提出不要在共享的Object.prototype中增加可 ...
- [Effective JavaScript 笔记]第31条:使用Object.getPrototypeOf函数而不要使用__proto__属性
ES5引入Object.getPrototypeOf函数作为获取对象原型的标准API,但由于之前的很多js引擎使用了一个特殊的__proto__属性来达到相同的目的.但有些浏览器并不支持这个__pro ...
随机推荐
- IT男的”幸福”生活"续5
各位哥们姐妹们,好,嗯,好.很多回复还是提到[IT男的”幸福”生活"]是小说来着剧情不真实,我还是免为其难的说一下吧.是真的回忆录,具体的事情都有.人也是真实的.在这里还是谢谢,这几天一直关 ...
- python3 入门 (四) 类与继承
Python 类 Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法. 对象可以包含任意数量和类型的数据. p ...
- 如何申请TexturePacker
对于很多做手机游戏的和用starling做页游的盆友,对TexturePacker应该并不陌生,但是呢,能免费申请注册码你造吗,你想要吗,TexturePacker的作者Adreas是个好人,只要你R ...
- “未能加载文件或程序集“EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”
产生原因: 使用nuget管理程序包,同一个解决方案里面有不同版本的Entity Framework,有可能在不同时间安装不同版本的Entity Framework,所以出现这个问题. 解决方案: 1 ...
- bootstrap tooltip 显示提示信息
参照网上文档,是这样说的, <a data-toggle="tooltip" data-placement="top" title="这是要提示 ...
- 年前辞职-WCF入门学习(1,2)
前言 周一的时候辞职了,离开了从12年毕业后8月份开始一直到现在的公司. 辞职之后当然是玩.玩了若干天的游戏,真的是没日没夜啊,但是玩的太坑,怒删游戏.话说上次玩还是在14年7月份.下次还是过年回家再 ...
- mysqldump使用方法
1.mysqldump的几种常用方法: (1)导出整个数据库(包括数据库中的数据) mysqldump -u username -p dbname > dbname.sql (2)导出数据库结构 ...
- 软工实践练习一——使用Git进行代码管理心得
在github.com的操作 注册 创建Organization 将指定代码库fork到小组Organization下 在Organization下创建repository 这些操作在学校的机房已经完 ...
- 【The final】软件工程实践总结
软件工程就这么告一段落了,竟然有那么一丢丢的舍不得-- 一.为拖延找的种种借口 [首先声明]以下纯粹是个人吐槽,仅作记录以便日后自己可以回顾一下往昔罢了,可以直接忽略,跳到第二大点:我的拖延之 ...
- 在Windows和UNIX下利用PHP和LDAP进行身份验证
我现在的老板曾要求我为企业内部的Web服务提供一种标准的身份验证方法.我遇到的一个主要问题就是我们公司主要使用了两种平台:UNIX和.所以,我的第一个想法并不很成功:它要求每个员工都使用UNIX或者L ...