对象的遍历
对象属性的遍历,方法有以下几种
- 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.map
,map方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。
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
方法返回false
,every
方法返回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
方法的第一个参数都是一个函数。该函数接受以下四个参数。
- 累积变量,默认为数组的第一个成员
- 当前变量,默认为数组的第二个成员
- 当前位置(从0开始)
- 原数组
这四个参数之中,只有前两个是必须的,后两个则是可选的。
如果要对累积变量指定初值,可以把它放在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
是唯一一个不等于自身的值。