[Effective JavaScript 笔记]第5章:数组和字典--个人总结

前言

这节里其实一直都在讨论对象这个在js中的万能的数据结构。对象可以表式为多种的形式,表示为字典和数组之间的区别。更多的我觉得这章讨论多的是一些对应实现功能的相关操作,有可能出现的bug以及如何避免来修复这些bug。比如下面会说到的for...in枚举属性的操作,可能因为对原型的一些操作,最终造成数据对象的操作的破坏。对于属性顺序有要求的如何处理,对类数组如何处理等。下面再一起一条一条回顾一下,这章里的主要内容。我觉得没必要讲的会一语带过,相比前面的几章,个人觉得这章的内容重点很少,注意几点就好,有几条只是强调一个知识点,有点繁琐。

第43条:使用Object的直接实例构造轻量级的字典

个人总结

这条里主要是强调在使用对象做为字典这种数据结构时,使用Object的直接实例来完成。
语法如下:

var obj=new Object();
//或var obj1={};

上面这样做的好处是在列举对象的属性时,按字典的说法就是键值key的时候,可以通过for...in循环来处理。

自定义字典类对象

如果这里用的是自定义的对象在使用for...in循环时,把添加到原型对象中的方法也做为key值返回了,和最初的程序需求不相符。

数组对象字典

数组对象也存在着和自定义类对象相的问题,有许多的ES5的数组方法,在有些环境中无法支持。会使用polyfill来对相应方法进行添加。最后也会污染到for...in循环。
基于上面的两种方式可能出现的问题。使用对象的直接实例可以把可能出现的问题控制在可控的区域。

提示

  • 使用对象字面量构建轻量级字典
  • 轻量级字典应该是Object.prototype的直接子类,以使for...in循环免受原型污染

第44条:使用null原型以防止原型污染

个人总结

基于上一条讲的,for...in枚举属性的问题,可以使用ES5中的Object.create方法,创建一个原型为null的对象。语法如下:

var obj=Object.create(null);

对于不支持Object.create方法的环境,只能使用非标准属性__proto__来兼容。

var o={__proto__:null};
o.instanceof Object;//false

可能有人会想下面的这种形式来解决相同的问题。但直接使用C.prototype=null,并不能完成。

function C(){}
C.prototype=null;
var c=new C();
Object.getPrototypeOf(c) === null;//false

使用null为原型来防止原型污染,使用技术如下:

  • 在开发时优先使用ES5的Object.create(null)
  • 在不支持ES5但支持非标准的__proto__属性的,使用obj.proto=null;
  • 其他环境提示无法完成相关的功能

提示

  • 在ES5环境中,使用Object.create(null)创建的自由原型的空对象是不太容易被污染的
  • 在一些较老环境中,考虑使用{__proto__:null}
  • __proto__既不标准,也不可移植,并且可能未来不再支持
  • 不要使用__proto__名作字典的key,因为一些环境将其作为特殊的属性对待

第45条:使用hasOwnProperty方法以避免原型污染

个人总结

第43,44条中讲到for...in枚举属性时,一直受到原型对象污染,无法只获取对象自身的属性。可以使用hasOwnProperty方法来对自身属性进行判断。hasOwnProperty本身是Object.prototype中的方法。所有对象都会继续这个方法。

对象直接调用hasOwnProperty方法

直接使用当前对象调用hasOwnProperty方法,有一个问题就是当前的对象或原型链中没有原型对象对这个方法进行重新的定义,才能保重代码的正确性。

使用函数的call方法对Object.prototype.hasOwnProperty方法进行处理
var hasOwn=Object.prototype.hasOwnProperty;
hasOwn.call(obj1);

可以保证hasOwnProperty在判断属性时的正确性。可以为需要的对象,把以上的判断封装在一个方法中方便调用。

对特殊的属性__proto__进行处理

使用记录状态的方法,来对非标准属性,进行代码兼容。防止环境不同,造成代码功能受到破坏。对于非定义的默认为false,详细代码到文章查看。

提示

  • 使用hasOwnProperty方法避免原型污染
  • 使用词法作用域和call方法避免覆盖hasOwnProperty方法
  • 考虑在封装hasOwnProperty测试样板代码的类中实现字典操作
  • 使用字典类避免将“__proto__”作为key来使用

第46条:使用数组而不要使用字典来存储有序集合

个人总结

因为字典的属性的读取是依赖于for...in循环的,而for...in循环在标准库中并没有规定相关属性的顺序,所以无法确切地知道属性输出的顺序。如果对于属性顺序的依赖性强,那么就不能使用字典来存储数据。而用数组存储,可以使用for循环,严格按照索引的顺序对数据进行处理。
本节有意思的是提到了浮点数的计算问题,浮点数的计算结果依赖于计算顺序,所以不能使用字典的形式也就是for...in循环的形式对各项进行加操作。解决办法是通过把浮点数转化为整数,然后把计算结果再转化为浮点数的方法,来解耦顺序依赖。

提示

  • 使用for...in循环来枚举对象的属性应当与顺序无关
  • 如果聚集运算字典中的数据,确保聚集操作与顺序无关
  • 使用数组而不是字典来存储有序集合

第47条:绝不要在Object.prototype中增加可枚举的属性

个人总结

还是和for...in循环有关,因为Object是所有js对象的根对象(Object.create(null)创建 的除外)。任何对象都是继承自Object.prototype对象的。对Object.prototype的操作,会影响到所有对象。如果非要添加方法或属性,可以使用Object.defineProperty方法,把属性或方法设置为不可枚举的。这样可以避免污染原型对象。

提示

  • 避免在Object.prototype中增加属性
  • 考虑编写一个函数代替Object.prototype中的方法
  • 如果确实需要在Object.prototype中增加属性,使用ES5中的Object.defineProperty方法将它们定义为不可枚举的属性。

第48条:避免在枚举期间修改对象

个人总结

还是和for...in循环有关,之前上面的已经讲过,for...in循环无法保证枚举属性的顺序。所以在修改对象属性时,可能枚举的是新的属性值,有可能是枚举的是旧的属性值。标准规定:如果被枚举的对象在枚举期间添加了新的属性,那么在枚举期间并不能保证新添加的属性能访问。
避免使用for...in循环执行这样的操作,使用其它替代的方案。比如自己管理循环控制,使用while循环来对对象进行相关操作。
本条中有相关有向图的遍历和查找相关的代码,到具体文章查看。

提示

  • 当使用for...in循环枚举一个对象的属性时,确保不要修改对象
  • 当迭代一个对象时,如果该对象的内容可能会在循环期间被改变,应用使用while循环或经典for循环来代替for...in循环
  • 为了在不断变化的数据结构中能够预测枚举,考虑使用一个有序的数据结构,例如数组,而不要使用字典对象

第49条:数组迭代要优先使用for循环而不是for...in循环

个人总结

for

for循环可以保证代码的执行过程的顺序,及可以根据索引去取确定的值。不会受其它因素的影响。

小知识点

对数组长度的存储,可以减少每次对数组长度的访问,提高循环的效率。

for(var i=0;i < arr.length;i++){}

其中var i=0;中执行一次,i < arr.length每次代码块执行前执行,i++代码块执行后执行。所以这里arr.length每一次都会去获取,使用下面这种方式更好:

for(var i=0,n=arr.length;i < n;i++){}
for...in

for...in循环可能会被原型链中的对象方法污染,无法保证其代码的正确性。

对比一下:

  • for循环清晰可控结果可预期
  • for...in循环不确定性强,容易被污染,顺序不可控

提示

  • 迭代数组的索引属性应当总是使用for循环而不是for...in循环
  • 考虑在循环之前将数组的长度存储在一个局部变量中以以避免重新计算数组长度

第50条:迭代方法优于循环

个人总结

得益于ES5里提供了功能强大的,众多的数组迭代方法,使得数组的处理更方便。主要包括filter,forEach,every,some,map等方法,具体详细可参阅对应的小节文章。
在使用for循环的时候,往往在边界条件设定错误,导致数组的溢出,或数组元素没被遍历的情况。对于这些问题都要小心去调试和对待,有时代码抽象层次高了之后,问题不容易被发现。
使用迭代方法,不用考虑边界条件的问题。主要考虑得是各个迭代方法的用法及它们之间的区别。利用不同的特性来完成个性化的需求。

通过猴子补丁给数组添加一些的新的迭代方法,比如这节里讲的takeWhile方法。
主要是功能,是提取出满足函数的数组的前几个元素。具体代码到文章里查看。

提示

  • 使用迭代方法替换for循环使得代码更可读,并且避免了重复循环控制逻辑
  • 使用自定义的迭代函数来抽象未被标准库支持的常见循环模式
  • 在需要提前终止的情况下,仍然推荐使用传统的for循环。some和every方法也可以用于提前退出

第51条:在类数组对象上利用通用的数组方法

个人总结

类数组对象中,大家经常使用的jQuery对象就是一个类数组的存在。也可以用这里的方法对它进行操作。如使用

[].slice.call($(‘a‘)).forEach(function(a){
  a.style.display=‘none‘;
})

这里只是举个例子,jquery里使用迭代方法的地方很多。
可以看看jquery里是怎样去完成类数组对象的构造的。主要是makeArray方法

//以下代码来自1.7.1版本function makeArray(array,results){
    var ret = results || [];
    if ( array != null ) {
      var type = jQuery.type( array );//这里只是一个类型的判断
      if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
        push.call( ret, array );
      } else {
        jQuery.merge( ret, array );//这里是主要的
      }
    }
    return ret;
}
//merge方法function merge( first, second ) {
  var i = first.length,//并不要求第一个是数组对象,有.length属性就成
      j = 0;
  if ( typeof second.length === "number" ) {//主要关注这里
    for ( var l = second.length; j < l; j++ ) {
      first[ i++ ] = second[ j ];
    }
  } else {
    while ( second[j] !== undefined ) {
      first[ i++ ] = second[ j++ ];
    }
  }
  first.length = i;
  return first;
}

可以看出上面的代码jquery本身是一个对象,并没有元素数组。而是通过一些方法获取到选择器对就的页面元素的数组后,利用merge方法把这些数组元素加到jquery对象上,并设置其length属性的长度,从而把jquery对象变成一个类数组了。
通过这个也可以引出类数组的满足的两条契约:

  • 具有一个范围在0到2^32-1的整形length属性
  • length属性大于该对象的最大索引。索引是一个范围在0到2^32-1的整数,它的字符串表示是该对象中的一个key

提示

  • 对于类数组对象,通过提取方法对象并使用其call方法来复用通用的Array方法
  • 任意一个具有索引属性和恰当length属性的对象都可以使用通用的Array方法

第52条:数组字面量优于数组构造函数

个人总结

这个没什么好说的,就是各种对应的数组,对象,函数等有构造函数,又有字面量方式的,使用字面量的方式更好。
防止构造函数命名被覆盖,无法完成正确功能。防止由于参数传递不同的值造成了不一样的结果。就是能用字面量就用字面量

提示

  • 如果数组构造函数的第一个参数是数字则数组的构造函数行为是不同的
  • 使用数组字面量替代数组构造函数

总结

这一章前半的条目,都是绕怎么解决对象字典的属性列举问题。列举属性使用的for...in循环的各种问题,原型污染,顺序不确定,枚举过程中操作的响应问题。后半主要讨论数组的相关操作问题,迭代方法优于循环,字面量优于构造函数,类数组的处理方法。通过这章学习,可以很明确地区分字典对象和数组之间的联系和区别,同时它们也是两种不同的数据结构。字典是无序,散列的键值对;数组是有序,索引的数据集合。

时间: 2024-10-17 01:52:10

[Effective JavaScript 笔记]第5章:数组和字典--个人总结的相关文章

[Effective JavaScript 笔记]第6章:库和API设计--个人总结

前言 又到了一章的总结,这章里的内容.是把我从一个代码的使用者,如何换位成一个代码的编写者.如何让别人用自己的代码更容易,不用去注意太多的无用细节,不用记住冗长的函数名.在使用API时怎样避免使用者会出现理解的偏差.如何处理一些特殊敏感的值,参数如何设置可以更好地自说明,如何减少API对状态的依赖,如何使API更加灵活,更利于用户的编写.下面一一展开介绍,对应的也会说明每条对应希望给到的是哪方面的建议! 第53条:保持一致的约定 个人总结 不要去创造一个独特的API,要使用一些大家惯用的词汇,参

[Effective JavaScript 笔记]第7章:并发--个人总结

前言 这一章的内容学到了事件队列和异步的API.js只是运行在其他应用程序的脚本语言.js即依赖于应用程序,也独立与应用程序.可以使它可以在多平台,多种环境上运行.ECMAScript标准中没有关于并发的说明.这章讨论的是一些常用的方法,使用事件和异步API是js编程的基础部分.异步API,有setTimeout,setInterval. 第61条:不要阻塞I/O事件队列 个人总结 js是构建在事件之上的单线程语言.js处理交互都以事件的方法进行传递的,监听事件的处理函数,都根据事件队列的执行相

[Effective JavaScript 笔记]第2章:变量作用域--个人总结

前言 第二章主要讲解各种变量作用域,通过这章的学习,接触到了很多之前没有接触过的东西,比如不经常用到的eval,命名函数表达式,with语句块等,下面是一个列表,我对各节的一点点个人总结,很多都是自己的收获和认识.可能有很多认识的误区,毕竟水平有限.如果有那里认识不对的地方,还希望可以在评论中指出来,这样可以得到大家在学习的帮助,也是很不错的. 第8条:尽量少用全局对象 个人总结: 解释全局对象在JS中定义的随意性,导致所有人都可以去定义去修改,导致命名冲突.对这样的全局对象进行依赖,导致不可知

[Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+1}" 反射获取函数源代码的功能很强大,使用函数对象的toString方法有严重的局限性.toString方法的局限性ECMAScript标准对函数对象的toString方法的返回结果(即该字符串)并没有任何要求.这意味着不同的js引擎将产生不同的字符串,甚至产生的字符串与该函数并不相关. 如果函数

[Effective JavaScript 笔记]全书总结

这本书中就像它前言中说的那样,这本书不是给初学者的.需要一定的基础,而且有一定的编码实践,才能很好的理解书里讲到的内容.学习一门编程语言,需要熟悉它的语法.形式和结构,这样才会编写合法的.符合语义的.具有意义和行为正确的程序.此书的目的是加深理解如何有效地使用js构建更可预测的.可靠和可维护的js应用程序和库.所有章节都是围绕着这几块来展开的. 目录 第1章 让自己习惯JavaScript 第1条:了解你使用的js版本 第2条:理解JavaScript的浮点数 第3条:当心隐式的强制转换 第4条

[Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式传递给eval函数以达到同样的功能.程序员面临一个选择:应该将代码表示为函数还是字符串?毫无疑问,应该将代码表示为函数.字符串表示代码不够灵活的一个重要原因是:它们不是闭包. 闭包回顾 看下面这个图 js的函数值包含了比调用它们时执行所需要的代码还要多的信息.而且js函数值还在内部存储它们可能会引用

[Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //"number" typeof "s";//"string" typeof null;//"object":ECMAScript把null描述为独特的类型,但返回值却是对象类型,有点困惑. 可以使用Object.prototype.t

《C++ Primer 4th》读书笔记 第4章-数组和指针

原创文章,转载请注明出处: http://www.cnblogs.com/DayByDay/p/3911573.html <C++ Primer 4th>读书笔记 第4章-数组和指针

Effective JavaScript Item 46 优先使用数组而不是Object类型来表示有顺序的集合

本系列作为Effective JavaScript的读书笔记. ECMAScript标准并没有规定对JavaScript的Object类型中的属性的存储顺序. 但是在使用for..in循环对Object中的属性进行遍历的时候,确实是需要依赖于某种顺序的.正因为ECMAScript没有对这个顺序进行明确地规范,所以每个JavaScript执行引擎都能够根据自身的特点进行实现,那么在不同的执行环境中就不能保证for..in循环的行为一致性了. 比如,以下代码在调用report方法时的结果就是不确定的