当前位置:首页 > 日记 > 正文

Javascript中八种遍历方法的执行速度深度对比

Javascript中八种遍历方法的执行速度深度对比

前言

遍历数组或对象是一名程序员的基本素养之一. 然而遍历却不是一件简单的事, 优秀的程序员知道怎么去选择合适的遍历方法, 优化遍历效率. 本篇将带你走进JavaScript遍历的世界, 享受分析JS循环的快感. 本篇所有代码都可以直接运行, 希望您通读本篇后, 不止是浏览, 最好是亲手去实践下.

概述

js有如下两种数据需要经常遍历

  • 数组(Array)
  • 对象(Object)

同时又提供了如下8种方法方便我们遍历元素

  • for
  • while(或do~while)
  • forEach
  • for in
  • $.each
  • $(selecter).each
  • map
  • every

最终我们将分析遍历效率选出最佳遍历选手.

本文将针对如下两种数据进行详细的分析和举栗. 下面举栗中如果不加特殊说明将会用到如下数据.

var array = ["囚徒","过客","领袖"];//职场3种人var o = {0:"linda",1:"style",2:"nick",length:3};

for

语法: for(初始化; 循环执行条件; 每遍历一个元素后做的事情;){}

(function(){//循环置于闭包之内 for(var i=0,length=array.length;i<length;i++){//缓存数组长度 console.log(array[i]);//内部方法若有可能相互影响,也要置于闭包之内 }})();

for循环只能遍历数组, 不能遍历对象. 写for循环时有两点需要注意.

  • 其一, 为了避免遍历时执行多遍计算数组长度的操作, 影响效率, 建议在循环开始以变量的形式缓存下数组长度, 若在循环内部有可能改变数组长度, 请务必慎重处理, 避免数组越界.
  • JavaScript中并没有类似java的块级作用域, for循环内部定义的变量会直接暴露在外(如 i,循环退出后,i变量将等于数组长度, 后续代码将能访问到 i 变量的值), 因此建议将for循环置于闭包内. 特别要注意的是: 如果在循环内部, 前一个元素的遍历有可能影响到后一个元素的遍历, 那么for循环内部方法也需要置于闭包之内.

do/while

语法: do{...}while(true);

 do while(function() { var i = 0, len = array.length; do { if (i == 2) { break; // 循环被终止, 此处如果是continue就会造成循环无法退出 }; console.log('array['+ i +']:' + array[i]); i++;//此句建议放置循环while头部 } while(i<len);})();

do/while的语法简化了循环的实现, 只保留对循环条件的判断, 所以我们要在循环内部构造出循环退出的条件, 否则有可能造成死循环. 特别要注意的是: 使用 continue 跳出本次遍历时, 要保证循环能够自动进入到下一次遍历, 因此保证循环走到下一次遍历的语句需要放到 continue 前面执行, 建议置于循环头部.(如上, i++ 语句最好放置循环头部)

do/while 循环与for循环大体差不多,只支持数组遍历, 多用于对循环退出条件不是很明确的场景. 一般来说不建议使用这种方式遍历数组.

forEach

语法: array.forEach(function(item){}) , 参数item表示数组每一项的元素

array.forEach(function(item){ if(item=="囚徒") return;//这里只能使用return跳过当前元素处理 console.log(item);});

forEach回调function默认有三个参数: item, index, array.

使用forEach循环有几点需要特别注意:

  • forEach无法遍历对象
  • forEach无法在IE中使用,只是在firefox和chrome中实现了该方法
  • forEach无法使用break,continue跳出循环,使用return时,效果和在for循环中使用continue一致

for in

语法: for(var item in array){}

for(var item in array){ console.log(item);}//0 1 2for(var item in o){ console.log(item);}//0 1 2 length

for in 可用于遍历数组和对象, 但它输出的只是数组的索引和对象的key, 我们可以通过索引和key取到对应的值. 如下:

for(var item in array){ console.log(array[item]);}//"囚徒" "过客" "领袖"for(var item in o){ console.log(o[item]);}//"linda" "style" "nick" "length"

$.each

语法: $.each(array|o, function(i, ele){}) 支持数组和对象

$.each(array, function(i, ele){ console.log(i,ele,this==ele);});//0 "囚徒" true//1 "过客" true//2 "领袖" true$.each(o, function(i, ele){ console.log(i,ele,this==ele);});//0 "linda" true//1 "style" true//2 "nick" true

这里我们注意到 this对象 指向当前属性的值,这是因为:

参考jQuery api:

$.each() 方法会迭代jQuery对象中的每一个DOM元素。每次回调函数执行时,会传递当前循环次数作为参数(从0开始计数)。更重要的是,回调函数是在当前DOM元素为上下文的语境中触发的。因此关键字 this 总是指向这个元素。
同时,上述遍历时, o 对象的属性中有一个length属性并没有被输出. 这是为什么呢? 请耐心往下看.

首先, 我们来看看遍历对象o时, 当前的this对象到底是什么?

$.each(o, function(i, ele){ if(this=="linda"){//我们随机选取第一个属性 console.log(this,this==ele); $.each(this, function(e, ele2) { console.log(e, ele2); }); }});//String {0: "l", 1: "i", 2: "n", 3: "d", 4: "a", length: 5, [[PrimitiveValue]]: "linda"} true//0 "l"//1 "i"//2 "n"//3 "d"//4 "a"

我们发现, this对象等于回调函数的第二个形参. 且它的 length 属性和 [[PrimitiveValue]] 属性并没有被打印出来, 为此我们来查看下length的内部属性.

$.each(o, function(i, ele){ if(this=="linda")//我们还是随机选取第一个属性(这还是随机吗?) console.log(Object.getOwnPropertyDescriptor(this, 'length'));});//Object {value: 5, writable: false, enumerable: false, configurable: false}

可见, this对象的length属性的 enumerable 属性被设置成了false, 这表示该对象不能被列举或遍历, 同时还不能被配置(configurable: false) , 也不能被赋值(writable: false) .

此时, 前面遍历 o 对象时,它的 length 属性没有被打印出来的疑问似乎有解了. 让我们来看看 o.length 的内部属性吧.

console.log(Object.getOwnPropertyDescriptor(o, 'length'));//Object {value: 3, writable: true, enumerable: true, configurable: true}

o.length 值为3, 可赋值, 可列举, 可配置. 这可不对, 刚刚不是说 enumerable 属性被设置成了false 才不会被遍历吗. 现在该值为 true, 并且还不可遍历. 这不合常理, 自然该有别的原因. 我们接着往下看.

var o = {0:"linda",1:"style",2:"nick",length:1}; // 试着改变length的值$.each(o, function(i, ele){//再遍历一次 console.log(i,ele);});//0 "linda"var o = {0:"linda",1:"style",2:"nick",length:5}; // 坚持改变length的值$.each(o, function(i, ele){//再遍历一次 console.log(i,ele);});// 0 linda// 1 style// 2 nick// length 5var o = {0:"linda",1:"style",2:"nick"}; // 试试去掉length属性$.each(o, function(i, ele){//再遍历一次 console.log(i,ele);});// 0 linda// 1 style// 2 nick

现象明了, 结合jquery源码, 当对象中存在length属性时, $.each 内部使用for循环去遍历对象, 否则它将使用for in循环去遍历, 因此$.each遍历对象遵循如下规律:

  • 如果对象中存在 length 属性, 遍历深度以length属性为准, 即length多大, 遍历多少个元素.
  • 如果对象中不存在 length 属性, 遍历深度以实际内部属性个数为准.

不仅如此, $.each的具体使用过程中还有以下几点需要注意:

  • 使用 return 或者 return true 为跳过一个元素,继续执行后面的循环;
  • 使用 return false 为终止循环的执行, 这是因为在 jquery.each 中, 若返回值指定为false, 才跳出循环, 如果感兴趣请翻看 jquery.each 源码;
  • 无法使用 break 与 continue 来跳过循环.

$(selecter).each

语法: $(selecter|array|o).each(function(i, ele){}) 支持数组和对象, 该方法基本上与$.each方法相同.

$('div').each(function(i,ele){ console.log(this,i,this == ele);});//dom... 0 dom.... true$(array).each(function(i,ele){//处理数组 if(this == "领袖") console.log(this,i,this == ele);});//String {0: "领", 1: "袖", length: 2, [[PrimitiveValue]]: "领袖"} 2 true$(o).each(function(i,ele){//处理对象 if(this == "nick") console.log(this,i,this == ele);});//String {0: "n", 1: "i", 2: "c", 3: "k", length: 4, [[PrimitiveValue]]: "nick"} 2 true

dom表示div元素, 由于this恒等ele, 说明this也表示div元素, 所以this并不是jquery对象, 而是普通的DOM对象(可以在this上随意使用DOM方法). 使用$(selecter).each方法,请注意以下几点:

  • i: 即序列值 ele: 表示当前被遍历的DOM元素
  • this 表示当前被遍历的DOM元素,不能调用jQuery方法, 如需调用jquery方法需要用$符号包裹.如, $(this)

map

Array.prototype.map,该方法只支持数组

语法: array.map(callback[,thisArg]) map方法使用其提供函数的每次返回结果生成一个新的数组.

var array = [1, 4, 9];var roots = array.map(Math.sqrt);//map包裹方法名// roots is now [1, 2, 3], array is still [1, 4, 9]var array = [1, 4, 9];var doubles = array.map(function(num) {//map包裹方法实体 return num * 2;});// doubles is now [2, 8, 18]. array is still [1, 4, 9]

实际上,由于map方法被设计成支持 [鸭式辨型][] , 该方法也可以用来处理形似数组的对象, 例如 NodeList.

var elems = document.querySelectorAll('select option:checked');var values = Array.prototype.map.call(elems, function(obj) { return obj.value;});

甚至还可以用来处理字符串, 如下:

var map = Array.prototype.map;var array = map.call('Hello 中国', function(x) {  return x.charCodeAt(0);});console.log(array);//[72, 101, 108, 108, 111, 32, 20013, 22269]

map处理字符串的方式多种多样, 例如 反转等.

var str = '12345';var output = Array.prototype.map.call(str, function(x) { return x;}).reverse().join('');console.log(output);//54321

例如 将字符串数组转换为数字数组, 只需一条语句, 如下:

console.log(['1', '2', '3'].map(Number));//[1,2,3]

目前map方法被大部分浏览器支持, 除了IE 6,7,8.

every

Array.prototype.every, 该方法同上述map方法也只支持数组

语法: arr.every(callback[, thisArg]) every 方法用于检验数组中的每一项是否符合某个条件, 若符合则放回true, 反之则返回false.

function isBigEnough(element, index, array) { return element >= 10;}[12, 5, 8, 130, 44].every(isBigEnough); // false[12, 54, 18, 130, 44].every(isBigEnough); // true

该方法还有简写方式, 如下:

[12, 5, 8, 130, 44].every(elem => elem >= 10); // false[12, 54, 18, 130, 44].every(elem => elem >= 10); // true

以上, 遍历数组和对象的8种方法简单的介绍完, 小结如下:

  • for in , $.each , $().each 既支持对象也支持数组遍历;
  • for , do/while , forEach 只支持数组;
  • Array.prototype.map, Array.prototype.every 只支持数组和形似数组的对象;
  • forEach不能退出循环,只能通过return来进入到下一个元素的遍历中(相当于for循环的continue), 且在IE没有实现该方法;
  • $.each和$().each循环只能通过return false 来退出循环, 使用return 或 return true 将跳过一个元素, 继续执行后面的循环.

测试各方法效率

下面我们来测试下上述方法的效率.

注: array数组默认为空, 依次赋值数组长度为1 000 000, 10 000 000, 100 000 000, 分别在 Chrome, Firefox, Safari 浏览器上进行两轮测试, 取测试时间平均值作为比较对象, 时间单位为ms. 如下是测试代码:

var array = [], length = array.length = 10000000;//(一千万)//for(var i=0;i<length;i++){// array[i] = 'louis';/

相关文章

javascript过滤数组重复元素的实现

javascript过滤数组重复元素的实现

重复元素,过滤,数组,方法,电脑软件,javascript过滤数组重复元素的实现方法 以下是在网上找的资料,直接在项目中可以使用,大家可以参考下:实现代码:function filterArray(receiveArray){var arrResult = new Array(); //定义一个返回结果…

JavaScript条件判断_动力节点Java

JavaScript条件判断_动力节点Java

条件判断,学院,节点,动力,电脑软件,JavaScript使用if () { ... } else { ... }来进行条件判断。例如,根据年龄显示不同内容,可以用if语句实现如下:var age = 20;if (age >= 18) { // 如果age >= 18为true,则执行if语句块 alert('adult');} e…

基于Bootstrap分页的实例讲解 | 必

基于Bootstrap分页的实例讲解 | 必

分页,必看,实例,电脑软件,Bootstrap,前面的话分页导航几乎在每个网站都可见,好的分页能给用户带来好的用户体验。本文将详细介绍Bootstrap分页概述在Bootstrap框架中提供了两种分页导航:? 带页码的分页导航? 带翻页的分页导航页码分页带页码…

Word中2003版进行设置页眉边距界的

Word中2003版进行设置页眉边距界的

边距,设置,页眉,操作方法,操作步骤,  word作为常用的办公软件,我们常常用它来处理一些资料,需要对打印出来的文档进行一个装订,为了避免装订好后页眉文字被覆盖,今天,小编就教大家在Word中2003版进行设置页眉边距界的操作方法。Word中2003版进…

怎么用word2010制作函数图像用word

怎么用word2010制作函数图像用word

图像,函数,方法,步骤,电脑软件,  WORD编辑中常常遇到画函数图形的问题,那么怎么在word中制作函数图像呢?下面小编来告诉你怎么用word2010制作函数图像吧。希望对你有帮助!word2010制作函数图像的步骤在WORD2010文档中,将光标定位在制作图形…

详解基于node的前端项目编译时内存

详解基于node的前端项目编译时内存

编译,内存溢出,项目,详解,电脑软件,前段时间公司有个基于vue的项目在运行npm run build的时候会报内存溢出,今天在某个技术流交群也有位小伙伴基于angular的项目也出现了这个问题,所以查了一些相关的资料总结了一下,下面会详细说明前端三大框…

基于vue.js实现侧边菜单栏

基于vue.js实现侧边菜单栏

菜单栏,侧边,电脑软件,vue,js,侧边菜单栏应该是很多项目里必不可少的 自己手写了一个 下面是效果图 下面就说一下实现的过程 还是比较简单的 首先导入一下需要的文件<link rel="stylesheet" type="text/css" href="bootstrap/css/boo…

ps如何将边缘模糊

ps如何将边缘模糊

边缘,模糊,方法,如何将,图片,  当图片的边缘不合适,我们可以用ps将边缘模糊。其实这个也不算太难,下面就让小编告诉你ps如何将图片边缘模糊的方法,一起学习吧。ps将图片边缘模糊的方法在photoshop cs2里,让人物的边缘部分变得稍微模糊一点的…

Node.js读取文件内容示例

Node.js读取文件内容示例

读取文件,示例,内容,电脑软件,Node,Node.js读取文件内容包括同步和异步两种方式。1、同步读取,调用的是readFileSyncvar rf=require("fs"); var data=rf.readFileSync("test","utf-8"); console.log(data); console.log("READ FILE S…

JavaScript数据结构之二叉树的查找

JavaScript数据结构之二叉树的查找

查找,数据结构,叉树,算法示例,之二,本文实例讲述了JavaScript数据结构之二叉树的查找算法。分享给大家供大家参考,具体如下:前面文章介绍了二叉树的遍历,现在谈谈在二叉树中进行查找。对二叉查找树来说,一般有以下三类查找:最大值,最小值和给定值…

通过VBA编程怎么实现Excel工作簿只

通过VBA编程怎么实现Excel工作簿只

编程实现,编程,只显示,工作,电脑软件,  当Excel工作簿中有多个工作表时,有时希望只显示当前正在操作的工作表,可以通过VBA编程来实现让Excel工作簿只显示指定的工作表。以下是小编为您带来的关于通过VBA编程实现Excel工作簿只显示指定工作…

Excel中2010版进行多行数据变成一

Excel中2010版进行多行数据变成一

排序,操作技巧,行数,操作步骤,电脑软件,  在编辑excel表格的时候,excel表格中有多行数据,有时候需要把多行数据变成一列进行分析。今天,小编就教大家在Excel中2010版进行多行数据变成一列并排序的操作技巧。Excel中2010版进行多行数据变成一…