JS遍历数组的方法总结

var arr = [ 'a', 'b', 'c' ];
var obj = { 'key1': 'a', 'key2': 'v2', 'key3': 'v3'}; 

1 for循环

提前储存length提升遍历的性能:

//其实我在实际使用中,常常因为提前储存了len而导致出错。视情况而定
for ( var i = 0, len = arr.length; i < len; i++){
    console.log(arr[i]);
}

还可以这么使用

for ( var i = 0, len = arr.length; i < len; i++);

比如只是纯粹为了计算 i 的值,可以省略掉 { } 用 ; 代替

2 for in

用来循环对象中非Symbol、可enumerable的属性。通过Array、Object创建的对象,所继承的non–enumerable的属性,不可遍历。比如,String的 indexOf() 方法 or Object toString() 方法.

这是一个无序遍历。

不应该去循环Array,因为Array的index顺序是必须的。但是for in不能保证这个index的顺序。最好使用forEach和for of来遍历数组

总的来说,最好不要在遍历期间,添加删除或修改属性。操作后不能确保这个属性还可以访问。

会遍历出原型链上的属性

Object.prototype.isTrue = 1;

for(var o in obj){
    console.log(o)
}
//会遍历出isTrue属性

可以使用hasOwnProperty来规避
for(var o in obj){
    if(obj.hasOwnProperty(o)){
        console.log(o)
    }
}
//如此不会打印出isTrue了

//或者使用getOwnPropertyNames()
Object.getOwnPropertyNames(obj)
//[ 'key1','key2','key3']

forEach

arr.forEach(function callback(currentValue[, index[, array]]) {
    //your iterator
}[, thisArg]);

thisArg决定了callback function的context,不传默认为undefined

callback处理的item必须在调用forEach之前确定,否则不会访问到。即使item的值在这段时间发生改变,callback也不会获知。如果是被删除,之后的循环不会遍历到。

除非抛异常,否则无法中断forEach函数。(break return 失效) 。如果你想break,可以使用以下function

  • A simple loop
  • A for…of loop
  • Array.prototype.every()
  • Array.prototype.some()
  • Array.prototype.find()
  • Array.prototype.findIndex()
arr.forEach((item, index, array) => {
  console.log(index, item);
});

使用this

扫描二维码关注公众号,回复: 3216793 查看本文章

function Counter() {
  this.sum = 0;
  this.count = 0;
}
Counter.prototype.add = function(array) {
  array.forEach(function(entry) {
    this.sum += entry;
    ++this.count;
  }, this);
  // ^---- Note
};

const obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3 
obj.sum;
// 16

copy函数的实现

function copy(obj) {
  const copy = Object.create(Object.getPrototypeOf(obj));//获取obj的prototype
  //getOwnPropertyNames获取可枚举和不可枚举的属性
  const propNames = Object.getOwnPropertyNames(obj);

  propNames.forEach(function(name) {
    //getOwnPropertyDescriptors 得到属性描述,不仅仅是包含value,还包含enumerable...
     { foo: { value: 123,
     writable: true,
     enumerable: true,
     configurable: true 
     }
    //重新给copy定义属性和描述
    Object.defineProperty(copy, name, desc);
  });

  return copy;
}

const obj1 = { a: 1, b: 2 };
const obj2 = copy(obj1); // obj2 looks like obj1 now

for…of

用来遍历 iterable 对象:String Array 类Array对象(arguments和NodeList, TypedArray, Map, Set,自定义的可遍历的对象)

let iterable = [10, 20, 30];

for (let value of iterable) {
  value += 1;
  console.log(value);
}
// 11
// 21
// 31

TypedArray

let iterable = new Uint8Array([0x00, 0xff]);

for (let value of iterable) {
  console.log(value);
}
// 0
// 255

Map

let iterable = new Map([['a', 1], ['b', 2], ['c', 3]]);

for (let entry of iterable) {
  console.log(entry);
}
// ['a', 1]
// ['b', 2]
// ['c', 3]

for (let [key, value] of iterable) {
  console.log(value);
}
// 1
// 2
// 3

Map和Object的区别:

  • 一个对象的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。
  • Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。
  • 你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。
  • Map 是可迭代的,而 Object 的迭代需要先获取它的键数组然后再进行迭代。
  • Object 都有自己的原型,所以原型链上的键名有可能和对象上的键名产生冲突。虽然 ES5 开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。
  • Map 在涉及频繁增删键值对的场景下会有些性能优势。

Set

let iterable = new Set([1, 1, 2, 2, 3, 3]);

for (let value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

arguments

(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);

// 1
// 2
// 3

DOM

// Note: This will only work in platforms that have
// implemented NodeList.prototype[Symbol.iterator]
let articleParagraphs = document.querySelectorAll('article > p');

for (let paragraph of articleParagraphs) {
  paragraph.classList.add('read');
}

generators

function* fibonacci() { // a generator function
  let [prev, curr] = [0, 1];
  while (true) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
}

for (let n of fibonacci()) {
  console.log(n);
  // truncate the sequence at 1000
  if (n >= 1000) {
    break;
  }
}

iterable objects

var iterable = {
  [Symbol.iterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

for (var value of iterable) {
  console.log(value);
}
// 0
// 1
// 2

跳出循环条件:break, continue, throw or return.

与for…in的区别

  • for…in 无序遍历可enumerable的对象的key(包含原型链和属性值)
  • for…of 遍历该对象的value
Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
//对象上定义的属性和原型链都被遍历出来了
//value不属于enumerable
  console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // logs 0, 1, 2, "foo"
  }
}

for (let i of iterable) {//只是对象内部的value
  console.log(i); // logs 3, 5, 7
}

map方法

返回一个新数组,不会修改原来的对象

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

与forEach相同,只是有return值

const map1 = array1.map(x => x * 2);


arr.map(function(value,index){
   return value++;
});
//求数组中每个元素的平方根

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

var kvArray = [{key: 1, value: 10}, 
               {key: 2, value: 20}, 
               {key: 3, value: 30}];

var reformattedArray = kvArray.map(function(obj) { 
   var rObj = {};
   rObj[obj.key] = obj.value;
   return rObj;
});

// reformattedArray 数组为: [{1: 10}, {2: 20}, {3: 30}], 

// kvArray 数组未被修改: 
// [{key: 1, value: 10}, 
//  {key: 2, value: 20}, 
//  {key: 3, value: 30}]
//获取字符串中每个字符所对应的 ASCII 码
var map = Array.prototype.map
var a = map.call("Hello World", function(x) { 
  return x.charCodeAt(0); 
})
// a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
//querySelectorAll 应用

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

总结:

  • for…in可以遍历对象及其原型链上,enumerable属性,可以遍历数组但是你别这么使用
  • for…of 遍历 iterable objects(String Array 类Array对象(arguments和NodeList, TypedArray, Map, Set,自定义的可遍历的对象))的value,可以break return。
  • forEach 遍历数组
  • map方法:遍历数组并返回一个新数组

猜你喜欢

转载自blog.csdn.net/jplyue/article/details/82314213