JavaScript中对象和数组的遍历方法

对象的遍历

对象属性的遍历,方法有以下几种

  • for...in
  • Object.keys()
  • Object.getOwnPropertyNames()

1. for...in

for...in语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性。

可枚举属性与不可枚举属性:
在JavaScript中,对象的属性分为可枚举和不可枚举之分,它们是由属性的enumerable值决定的。
可枚举性决定了这个属性能否被for…in查找遍历到。
js中基本包装类型的原型属性是不可枚举的,如Object, Array, Number等,
如果你写出这样的代码遍历其中的属性:

var num = new Number();
for(var pro in num) {
    console.log("num." + pro + " = " + num[pro]);
}
//输出空
这是因为Number中内置的属性是不可枚举的,所以不能被for…in访问到。
语法
var obj = {
    name: 'jack',
    age: 33
}
for (var n in obj) {
    console.log(n)
}
输出:name, age

注意:

通常为了只枚举对象的私有属性,会用一个判断函数hasOwnProperty()来过滤掉原型中的属性

Object.prototype.hasOwnProperty方法接受一个字符串作为参数,返回一个布尔值,表示该实例对象自身是否具有该属性。

例1:
Object.prototype.clone = function() {}
var obj = {
    name: 'jack',
    age: 33
}
for (var n in obj) {
    console.log(n)
}
//多出了在原型上定义的方法
输出:name, age, clone

例2:
Object.prototype.clone = function () {};
var obj = {
  name: "jack",
  age: 33,
};
for (var n in obj) {
  if (obj.hasOwnProperty(key)) {
    console.log(n);
  }
}

输出:name, age
#推荐总是使用 hasOwnProperty 方法,这将会避免原型对象扩展带来的干扰:

2. Object.keys() , Object.getOwnPropertyNames()

Object.keys方法的参数是一个对象,返回一个数组。该数组的成员都是该对象自身的(而不是继承的)所有属性名。

一般情况下,几乎总是使用Object.keys方法,遍历对象的属性。

语法
var obj = {
  p1: 123,
  p2: 456
};

Object.keys(obj) // ["p1", "p2"]

var obj = {
  p1: 123,
  p2: 456
};

Object.getOwnPropertyNames(obj) // ["p1", "p2"]

对于一般的对象来说,Object.keys()Object.getOwnPropertyNames()返回的结果是一样的。只有涉及不可枚举属性时,才会有不一样的结果。
Object.keys方法只返回可枚举的属性(详见《对象属性的描述对象》一章),Object.getOwnPropertyNames方法还返回不可枚举的属性名。

var a = ['Hello', 'World'];

Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]
//上面代码中,数组的length属性是不可枚举的属性,所以只出现在Object.getOwnPropertyNames方法的返回结果中。

小结

  • for..in 遍历的是对象的可枚举属性,包括原型
  • Object.keys 遍历的是对象可枚举属性,不包括原型
  • Object.getOwnPropertNames 遍历的是对象的所有属性,不包括原型

数组项的全部遍历

全部遍历所列举的方法,是指主要用来枚举数组各项的方法。

1. for循环

for循环遍历是最原始,也是性能最高的一种遍历方法。

var arr = [1,4,7,9]
for (var i = 0, len = arr.length; i < len; i++) {
    console.log(arr[i]) // 输出 1 4 7 9
}

2. forEach函数

forEach()函数是构造函数Array的原型上的函数,即Array.prototype.forEach,因此所有数组都可以用这个方法。
函数没有返回值,因此主要用来对数组的过程处理。

forEach方法的回调函数有三个参数,elem为当前成员的值,index为当前成员的位置,arr为原数组([1, 2, 3])。
var arr = ['a', 'b', 'c']
arr.forEach((elem, index, arr) => { // 处理函数作为参数
    console.log(index + ' is ' + elem)
})
输出
0 is a
1 is b
2 is c

3. map函数

map()函数也是构造函数Array的原型上的函数,即Array.prototype.mapmap方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。

map方法向它传入三个参数:当前成员、当前位置和数组本身。
[1, 2, 3].map((elem, index, arr)=> {
  return elem * index;
});
// [0, 2, 6]

4. for...of

for...of是ES6新增的语法,它不仅可以用来遍历数组,还可以遍历字符串,以及ES6中的Map,Set。

var array = ['a', 'b', 'c']
for (let item of array) {
    console.log(item) // 输出 a b c
}

数组项的其他遍历

1. filter()函数

filter方法用于过滤数组成员,满足条件的成员组成一个新数组返回。

它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。

filter方法的参数函数可以接受三个参数:当前成员,当前位置和整个数组。
[1, 2, 3, 4, 5].filter( elem=> {
  return (elem > 3);
})
//输出 [4, 5]

2. some(),every()

这两个方法类似“断言”(assert),返回一个布尔值,表示判断数组成员是否符合某种条件。

它们接受一个函数作为参数,所有数组成员依次执行该函数。该函数接受三个参数:当前成员、当前位置和整个数组,然后返回一个布尔值。

some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false

代码中,如果数组`arr`有一个成员大于等于3,`some`方法就返回`true`
var arr = [1, 2, 3, 4, 5];
arr.some( (elem, index, arr)=> {
  return elem >= 3;
});
// true

every方法是所有成员的返回值都是true,整个every方法才返回true,否则返回false

代码中,数组`arr`并非所有成员大于等于`3`,所以返回`false`。
var arr = [1, 2, 3, 4, 5];
arr.every(function (elem, index, arr) {
  return elem >= 3;
});
// false

注意,对于空数组,some方法返回falseevery方法返回true,回调函数都不会执行。

3. reduce(),reduceRight()

reduce方法和reduceRight方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,reduce是从左到右处理(从第一个成员到最后一个成员),reduceRight则是从右到左(从最后一个成员到第一个成员),其他完全一样。

[1, 2, 3, 4, 5].reduce(function (a, b) {
  console.log(a, b);
  return a + b;
})
// 1 2
// 3 3
// 6 4
// 10 5
//最后结果:15

上面代码中,reduce方法求出数组所有成员的和。整个方法的返回值就是最后一轮的返回值15

reduce方法和reduceRight方法的第一个参数都是一个函数。该函数接受以下四个参数。

  1. 累积变量,默认为数组的第一个成员
  2. 当前变量,默认为数组的第二个成员
  3. 当前位置(从0开始)
  4. 原数组

这四个参数之中,只有前两个是必须的,后两个则是可选的。

如果要对累积变量指定初值,可以把它放在reduce方法和reduceRight方法的第二个参数。

[1, 2, 3, 4, 5].reduce(function (a, b) {
  return a + b;
}, 10);
// 25

上面代码指定参数a的初值为10,所以数组从10开始累加,最终结果为25。注意,这时b是从数组的第一个成员开始遍历。

上面的第二个参数相当于设定了默认值,处理空数组时尤其有用。

function add(prev, cur) {
  return prev + cur;
}

[].reduce(add)
//错误:没有初始值的空数组
// TypeError: Reduce of empty array with no initial value

[].reduce(add, 1)
// 1

上面代码中,由于空数组取不到初始值,reduce方法会报错。这时,加上第二个参数,就能保证总是会返回一个值。

由于这两个方法会遍历数组,所以实际上还可以用来做一些遍历相关的操作。比如,找出字符长度最长的数组成员。

function findLongest(entries) {
  return entries.reduce(function (longest, entry) {
    return entry.length > longest.length ? entry : longest;
  }, '');
}

findLongest(['aaa', 'bb', 'c']) // "aaa"

上面代码中,reduce的参数函数会将字符长度较长的那个数组成员,作为累积值。这导致遍历所有成员之后,累积值就是字符长度最长的那个成员。

4. indexOf(),lastIndexOf()

indexOf方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1

var a = ['a', 'b', 'c'];

a.indexOf('b') // 1
a.indexOf('y') // -1

indexOf方法还可以接受第二个参数,表示搜索的开始位置。

['a', 'b', 'c'].indexOf('a', 1) // -1

上面代码从1号位置开始搜索字符a,结果为-1,表示没有搜索到。

lastIndexOf方法返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1

var a = [2, 5, 9, 2];
a.lastIndexOf(2) // 3
a.lastIndexOf(7) // -1

注意,这两个方法不能用来搜索NaN的位置,即它们无法确定数组成员是否包含NaN

[NaN].indexOf(NaN) // -1
[NaN].lastIndexOf(NaN) // -1

这是因为这两个方法内部,使用严格相等运算符(===)进行比较,而NaN是唯一一个不等于自身的值。

posted @ 2020-04-14 17:38  仲吕  阅读(376)  评论(0编辑  收藏  举报