1、JavaScript 中的可枚举属性与不可枚举属性
在 JavaScript 中,对象的属性分为可枚举和不可枚举之分,它们是由属性的 enumerable
值决定的。可枚举性决定了这个属性能否被 for…in
查找遍历到。
可枚举属性是指那些内部 “可枚举” 标志设置为 true 的属性,对于通过直接的赋值和属性初始化的属性,该标识值默认为即为 true,对于通过 Object.defineProperty
等定义的属性,该标识值默认为 false。可枚举的属性可以通过 for...in
循环进行遍历(除非该属性名是一个 Symbol)。
1.1 怎么判断属性是否可枚举
js 中基本包装类型的原型属性是不可枚举的,如 Object,Array,Number等,如果你写出这样的代码遍历其中的属性:
var num = new Number();
for (var pro in num) {
console.log("num." + pro + " = " + num[pro]);
}
它的输出结果会是空。这是因为 Number 中内置的属性是不可枚举的,所以不能被 for…in 访问到。
Object 对象的 propertyIsEnumerable()
方法可以判断此对象是否包含某个属性,并且这个属性是否可枚举。
需要注意的是:如果判断的属性存在于 Object 对象的原型内,不管它是否可枚举都会返回 false。
1.2 Object.prototype.propertyIsEnumerable()
每个对象都有一个 propertyIsEnumerable
方法。此方法可以确定对象中指定的属性是否可以被 for…in 循环枚举,但是通过原型链继承的属性除外。如果对象没有指定的属性,则此方法返回 false。
语法:
obj.propertyIsEnumerable(prop)
参数:
prop:需要测试的属性名。
返回值:
propertyIsEnumerable() 方法返回一个布尔值,表示指定的属性是否可枚举。
下面的例子演示了 propertyIsEnumerable 方法在普通对象和数组上的基本用法:
var o = {};
var a = [];
o.prop = 'is enumerable';
a[0] = 'is enumerable';
o.propertyIsEnumerable('prop'); // 返回 true
a.propertyIsEnumerable(0); // 返回 true
下面的例子演示了用户自定义对象和内置对象上属性可枚举性的区别。
var a = ['is enumerable'];
a.propertyIsEnumerable(0); // 返回 true
a.propertyIsEnumerable('length'); // 返回 false
Math.propertyIsEnumerable('random'); // 返回 false
this.propertyIsEnumerable('Math'); // 返回 false
参考:Object.prototype.propertyIsEnumerable()
2、JavaScript 中数组遍历
2.1 普通 for 循环,经常用的数组遍历,遍历值为数组元素索引
(1)普通 for 循环
var arr = ["red", "green", "blue", "yellow", "orange"];
for (let i = 0; i < arr.length; i++) {
console.log(i); // 0、1、2、3、4
console.log(arr[i]); // "red" "green" "blue" "yellow" "orange"
}
(2)优化版 for 循环
使用变量,将长度缓存起来,避免重复获取长度,数组很大时优化效果明显。
var arr = ["red", "green", "blue", "yellow", "orange"];
for (let i = 0, len = arr.length; i < len; i++) {
console.log(i, arr[i]); // 0 "red" 1 "green" 2 "blue" 3 "yellow" 4 "orange"
}
2.2 forEach 遍历,数组自带的循环,主要功能是遍历数组
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。
注意: forEach() 对于空数组是不会执行回调函数的。
语法:
array.forEach(function(currentValue, index, arr), thisValue)
参数:
- currentValue:必需。当前元素
- index:可选。当前元素的索引值。
- arr:可选。当前元素所属的数组对象。
- thisValue:可选。传递给函数的值一般用 “this” 值。如果这个参数为空, “undefined” 会传递给 “this” 值。
返回值: undefined。
forEach():
- 没有返回值,本质上等同于 for 循环,对每一项执行 function 函数。即map是返回一个新数组,原数组不变,forEach
是改变原数组。 - 不支持 continue,用 return false 或 return true 代替。使用 return 语句实现的是 continue 关键字的效果
- 不支持 break,用 try catch/every/some 代替。
var arr = ["red", "green", "blue", "yellow", "orange"];
arr.forEach(function (item, index) {
console.log(index, item); // 0 "red" 1 "green" 2 "blue" 3 "yellow" 4 "orange"
});
2.3 map 遍历,map 即是 “映射”的意思,用法与 forEach 相似
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
map() 方法按照原始数组元素顺序依次处理元素。
注意:
- map() 不会对空数组进行检测。
- map() 不会改变原始数组。
- map() 遍历支持使用 return 语句,支持 return 返回。
var arr1 = [1, 2, 3, 4, 5];
// 第一个参数为数组元素,第二个参数为数组元素索引,第三个参数为数组本身(可选)
var arr2 = arr1.map(function (item, index) {
return item * 2;
});
console.log(arr1); // [1, 2, 3, 4, 5]
console.log(arr2); // [2, 4, 6, 8, 10]
2.4 for-of 遍历
for..of
为 ES6 新增的方法,主要来遍历可迭代的对象(包括 Array,Map,Set,String,TypedArray,arguments等),它主要用来获取对象的属性值,而 for…in 主要获取对象的属性名。
(1)用于遍历数组时,遍历值为数组元素。(推荐用于遍历数组)
var arr1 = [1, 2, 3, 4, 5];
for (let item of arr1) {
console.log(item); // 1、2、3、4、5
}
// 遍历普通对象报错 TypeError: obj is not iterable
var obj = { "a": 1, "b": 2, "c": 3 };
for (let val of obj) {
console.log(val);
}
(2)for..of
用于循环对象时,须配合 Object.keys()
一起使用,直接用于循环对象会报错,不推荐使用 for…of 循环对象,循环值为对象属性。
var obj = { "a": 1, "b": 2, "c": 3 };
for (let val of Object.keys(obj)) {
console.log(val); // "a" "b" "c"
}
总结:
- for-of 这个方法避开了 for-in 循环的所有缺陷。它可以正确响应 break、continue 和 return 语句。
- for-of 循环支持数组,还支持类对象(例如 DOM NodeList 对象),字符串,map 对象,set 对象。
- for -of 遍历数组,遍历的结果是数组的值。
- for-of 不适合遍历普通对象,但可以通过
Array.from()
方法转换为类数组或可迭代对象。
3、JavaScript 中对象遍历
3.1 for-in 遍历对象
for...in
遍历对象,它遍历的是属性名。会遍历实例的属性,还会遍历整个原型链,这可能不是你所期望的结果,然后从性能角度上看 Object.keys() 会更优。
循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
var obj = { "a": 1, "b": 2, "c": 3, 9: "ok" };
// for-in遍历,会把属性名排序
for (let key in obj) {
// key 为属性名, obj[key] 为属性值
console.log(key); // "9" "a" "b" "c"
console.log(obj[key]); // "ok" 1 2 3
}
for-in
是为遍历对象而设计的,不适用于遍历数组。
遍历数组的缺点:for-in 遍历的 index 值"0",“1”,"2"等是字符串。
var arr = ["red", "green", "blue", "yellow", "orange"];
for (let index in arr) {
console.log(arr[index]); // "red" "green" "blue" "yellow" "orange"
console.log(index); // "0" "1" "2" "3" "4"
}
for…in 总结:
- for in 遍历对象时,会遍历实例的属性+还遍历原型中可枚举的属性。
- for in 不适合遍历数组,遍历数组遍历的是下标,下标类型是 字符串。
- for in 遍历会以任意顺序遍历对象的属性名。
3.2 Object.keys(obj) 遍历对象
Object.keys()
方法的参数是一个对象,返回一个数组。该数组的成员都是该对象自身的(而不是继承的)所有属性名,且只返回可枚举的属性(不含 Symbol 属性)。
var obj = { "a": 1, "b": 2, "c": 3, 9: "ok" };
var arr3 = Object.keys(obj);
console.log(arr3); // ["9", "a", "b", "c"]
Object.keys().forEach() 遍历对象,把对象的属性名转成数组,可与数组遍历结合使用。
3.3 Object.getOwnPropertyNames(obj) 遍历
Object.getOwnPropertyNames()
方法与 Object.keys() 类似,也是接受一个对象作为参数,返回一个数组,包含了该对象自身的所有属性名(不含 Symbol 属性,但是包括不可枚举属性)。但它能返回不可枚举的属性。
var a = ['Hello', 'World'];
console.log(Object.keys(a)); // ["0", "1"]
console.log(Object.getOwnPropertyNames(a)); // ["0", "1", "length"]
var obj = { "a": 1, "b": 2, "c": 3, 9: "ok" };
console.log(Object.getOwnPropertyNames(obj)); // ["9", "a", "b", "c"]
上面代码中,数组的 length 属性是不可枚举的属性,所以只出现在Object.getOwnPropertyNames
方法的返回结果中。
由于 JavaScript 没有提供计算对象属性个数的方法,所以可以用这两个方法代替。
var obj = { "a": 1, "b": 2, "c": 3, 9: "ok" };
console.log(Object.keys(obj).length); // 4
console.log(Object.getOwnPropertyNames(obj).length); // 4
3.4 Reflect.ownKeys(obj) 遍历
静态方法 Reflect.ownKeys()
返回一个由目标对象自身的属性键组成的数组。包含对象自身的所有属性,不管属性名是 Symbol 或字符串,也不管是否可枚举。
如果目标不是 Object,抛出一个 TypeError。
Reflect.ownKeys({z: 3, y: 2, x: 1}); // [ "z", "y", "x" ]
Reflect.ownKeys([]); // ["length"]
var sym = Symbol.for("comet");
var sym2 = Symbol.for("meteor");
var obj = {[sym]: 0, "str": 0, "773": 0, "0": 0,
[sym2]: 0, "-1": 0, "8": 0, "second str": 0};
Reflect.ownKeys(obj);
// [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]
// Indexes in numeric order,
// strings in insertion order,
// symbols in insertion order
4、使用 JQuery 的遍历
4.1 jQuery 的 $.each()
jQuery 的遍历方法通常被用来遍历 DOM 元素,用于数组和对象的是 $.each()
方法,它接受两个参数,分别指代属性名/数组索引和属性值/数组元素:
/****$.each()遍历对象和数组****/
$.each(arrTmp, function(index, value){
console.log(index +": "+ value);
});
$.each(objTmp, function(key, value){
console.log(key + ": " + value);
});
4.2 $.grep() 筛选遍历数组
$.grep()
函数使用指定的函数过滤数组中的元素,并返回过滤后的数组。
提示:源数组不会受到影响,过滤结果只反映在返回的结果数组中。
语法:
$.grep( array, function [, invert ] )
$.grep(nums, function (num, index) {
// num = 数组元素的当前值
// index = 当前值的下标
//你想要的操作
});