JavaScript中常用遍历方法总结


title: CodeReview问题与总结第一期
date: 2018-09-11 16:00
tags: [CodeReview],[总结]

categories: 总结

常用的遍历方法有哪些?每种适用于什么场景,各有什么优缺点?

常用遍历方法

for循环

语法:

for (语句 1; 语句 2; 语句 3) { 被执行的代码块 }
语句 1 在循环(代码块)开始前执行
语句 2 定义运行循环(代码块)的条件
语句 3 在循环(代码块)已被执行之后执行

for…in

for...in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。

语法:

for (variable in object) {…}
variable:在每次迭代时,将不同的属性名分配给变量。
object:被迭代枚举其属性的对象。

注意:for...in不应该用于迭代一个 Array

  • for...in不仅可以遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。(即:for...in循环语句将返回所有可枚举属性,包括非整数类型的名称和继承的那些。)
  • 不能保证for...in将以任何特定的顺序返回索引。

应用:

var obj = {a:1, b:2, c:3};

for (var prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}
for…of

for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

语法:

for (variable of iterable) {
//statements
}
variable:在每次迭代中,将不同属性的值分配给变量。
iterable:可枚举其枚举属性的对象。

注意:可以使用break跳出循环
应用:

let iterable = [10, 20, 30];

for(let a of iterable){
  if(a > 20) {
    break
  }
    console.log(a)
}
// 10
// 20
forEach()

forEach方法对数组的每个元素执行一次提供的函数。
语法:

array.forEach(callback(currentValue, index, array){
//do something
}, this)
array.forEach(callback[, thisArg])

参数:

currentValue(当前值):数组中正在处理的当前元素。
index(索引):数组中正在处理的当前元素的索引。
array:forEach()方法正在操作的数组。
thisArg:可选参数。当执行回调 函数时用作this的值(参考对象)。

应用:

//打印数组
const arr = ['a', 'b', 'c'];

arr.forEach( element => console.log(element));
//复制对象
function copy(obj) {
  var copy = Object.create(Object.getPrototypeOf(obj));
  var propNames = Object.getOwnPropertyNames(obj);

  propNames.forEach(function(name) {
    var desc = Object.getOwnPropertyDescriptor(obj, name);
    Object.defineProperty(copy, name, desc);
  });

  return copy;
}

var obj1 = { a: 1, b: 2 };
var obj2 = copy(obj1); 

缺点:
不能中途跳出循环,如果数组在迭代时被修改了,则其他元素会被跳过。

var words = ["one", "two", "three", "four"];
words.forEach(function(word) {
  console.log(word);
  if (word === "two") {
    words.shift();
  }
});
// one
// two
// four
map()

map方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
语法:

arr.map(function callback(currentValue, index, array) {
// Return element for new_array
}[, thisArg])

参数:

callback: 生成新数组元素的函数,使用三个参数:
currentValue callback: 的第一个参数,数组中正在处理的当前元素。
index callback: 的第二个参数,数组中正在处理的当前元素的索引。
array callback: 的第三个参数,map方法被调用的数组。
thisArg: 可选的。执行 callback 函数时 使用的this 值。

返回值:
一个新数组,每个元素都是回调函数的结果。

应用:

//重新格式化数组
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]

缺点: 不能中途跳出循环。

性能测试

测试环境:node v9.8.0
测试代码:

测试环境:node v9.8.0

JavaScript
var number = 100; // array大小
var array = [];
for (let i = 0; i < number; i++) {
    array[i] = i + 1;
}
var len = array.length;

console.log('数组长度为:', number)
// 正常for循环
console.time('normal for');
for (let i = 0; i < len; i++) {
    array[i] + 1;
}

console.timeEnd('normal for');

// 倒序for循环
console.time('reverse for');
for (let i = len - 1; i--;) {
    array[i] + 1;
}
console.timeEnd('reverse for');

// while循环
console.time('while');
let i = 0;
while (i < len) {
    array[i] + 1;
    i++;
}
console.timeEnd('while');

// for-in循环
console.time('for-in');
for (let i in array) {
    array[i] + 1;
}
console.timeEnd('for-in');

// for each 循环
console.time("for each");
array.forEach(e => e + 1);
console.timeEnd("for each")

// map循环
console.time("map");
array.map(e => e + 1);
console.timeEnd("map")

测试结果(ms为单位):

数组长度1001000100001000001000000
普通for循环0.1010.1250.3430.2244.778
倒序for循环0.0090.0300.2558.6064.391
while循环0.0070.0280.2423.9574.445
for-in0.0320.3512.46622.203163.261
for-each0.0300.0540.2961.98614.921
map0.0290.0550.32319.905189.387

思考:我们选择循环的多个角度——业务需要、代码可读性、性能。

延伸阅读: 为什么for-in效率相比而言这么低?
for...in 一般是用在对象属性名的遍历上的,由于每次迭代操作会同时搜索实例本身的属性以及原型链上的属性,所以效率肯定低下。for-in实际上效率是最低的。这是因为 for...in有一些特殊的要求,具体包括:

  1. 遍历所有属性,不仅是 ownproperties 也包括原型链上的所有属性。
  2. 忽略 enumerable(可枚举) 为 false 的属性。
  3. 必须按特定顺序遍历,先遍历所有数字键,然后按照创建属性的顺序遍历剩下的。

相关链接:
吹毛求疵的追求优雅高性能JavaScript
Array-MDN

Vue是如何实现响应式的?监测变化时应注意什么?

如何追踪变化

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。
每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

vue

监测变化时应注意:
1. Vue 不能检测到对象属性的添加或删除。

var vm = new Vue({
     data: {
       a: 1
     }
   })
   // `vm.a` 现在是响应式的

   vm.b = 2
   // `vm.b` 不是响应式的
  1. 不能检测以下变动的数组:
    • 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
    • 当你修改数组的长度时,例如:vm.items.length = newLength

相关链接:
Vue细节与最佳实践
Vue对象更改检测注意事项

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值