数组的遍历
假设只需要简单的把数组遍历一遍,比较优雅的做法是使用while循环。
var arr = [1,2,3];
var i = arr.length;
while(i--){
console.log(arr[i]);
}
但这是从末尾开始倒着进行,循环当中往往还要运行函数,因此还是经典的for循环用得比较普遍。简单封装一个用于遍历数组的函数:
var each = function(obj ,callback){
if(obj instanceof Array){
for(var i = 0, length = obj.length; i < length; i++){
var result = callback.call(undefined, i, obj[i], obj);
if(result === false){
break;
}else if(result === true){
continue;
}
}
}
}
回调中传递三个参数,分别是当前索引,当前值,整个数组,如果返回false,中止循环(break),返回true,停止当前循环(continue)。跟ECMA-262中的forEach()一样,也就是说,在IE9以上浏览器中,可以直接使用arr.forEach()来进行遍历,但forEach不能中止循环或停止当前循环,会一直遍历完全部。
var arr = [1,2,3];
each(arr, function(index, value){
if(value === 2){
return false;
}
console.log(value);
})
//以上输出:
1
一维数组如此遍历,多维就再调用一遍吧。
遍历对象
对象的遍历不像数组那样简单,这是因为对象的属性有多种,从是否可枚举角度看有可枚举属性与不可枚举属性之分,从是否为自身属性来看有自身属性与继承属性(原型链上的)之分。我们先定义一个具备这四种属性的对象:
var Person = function(){
this.name="小明"
}
Person.prototype.sex = "man";
var myObj = new Person();
myObj.hasOwnProperty = true;
Object.defineProperty(myObj, "id", {value: "1", enumerable: false});
定义一个对象myObj,有自身可枚举属性name,hasOwnProperty,继承属性sex,不可枚举属性id。先来看咱们熟知的for in能把哪些属性找出来。
var forInArr = [];
for(var i in myObj){
forInArr.push(i);
}
console.log("for in:",forInArr.join(','));//name,hasOwnProperty,sex
可以看到,一向被认为很“厉害”的for in能找到继承属性sex,却也无法找到不可枚举属性id。
因此,需要遍历自身和原型上的可枚举属性,使用for in即可!
如果只需要自身的可枚举属性,可以使用Object.keys(myObj),当然这需要IE9或以上;也可以在for in中过滤掉原型上的,使用hasOwnProperty()方法:
var forInArr = [];
for(var i in myObj){
if(Object.prototype.hasOwnProperty.call(myObj, i)){
forInArr.push(i);
}
}
console.log("for in hasOwnproperty:",forInArr.join(','));//name,hasOwnProperty
不可枚举的属性要怎么找到?IE9或以上可以使用Object.getOwnPropertyNames(myObj),注意不是Object.prototype上的。
console.log(Object.getOwnPropertyNames(myObj));// ["name", "hasOwnProperty", "id"]
getOwnPropertyNames()可以获得自身的所有属性,包括可枚举和不可枚举,原型上的获得不到。那原型上的不可枚举属性如何获得(for in只能获得可枚举的)?其实还是使用它,只是得从对象的构造函数的原型对象上取:
console.log(Object.getOwnPropertyNames(myObj.constructor.prototype));// ["constructor", "sex"]
sex是我们自定义的,constructor是自带的。其实我们要遍历的对象一般都是{}大括号包裹的简单对象,for in循环结合hasOwnProperty简单又实用。
遍历DOM集合
我自定义DOM集合为通过getElementsByClassName,getElementsByTagName,querySelectorAll,children等方法获得的元素集合。如下边选取ul#ul下的li.li-class几种常见办法:
- 这是li
- 这是li
- 这是li
- 这是li
- 这是li
var liList = document.getElementById("ul").getElementsByTagName("li");//HTMLCollection
var liList = document.getElementById("ul").getElementsByClassName("li-class");//IE 9+,HTMLCollection
var liList = document.getElementById("ul").querySelectorAll(".li-class");IE 8+,NodeList
var liList = document.getElementById("ul").children;//IE 8+,HTMLCollection
var liList = document.querySelectorAll("#ul>li")//NodeList,IE8+
querySelector/querySelectorAll需要IE8或更高版本的浏览器,返回的是NodeList对象。常规的byTagName,children,byClassName返回的则是HTMLCollection对象,byClassName需要IE9或以上,其他在IE8下均能工作。
要遍历返回的对象,可以把它们转化为真正的数组,就能应用上边的遍历方法了。但其实没必要,返回的对象本来直接for循环就可以遍历,在each方法中的if判断中把HTMLCollection和NodeList添加进去即可。
综上,遍历数组、对象以及DOM集合的简单方法:
var each = function(obj ,callback){
if(obj instanceof Array || obj instanceof HTMLCollection || obj instanceof NodeList){
for(var i = 0, length = obj.length; i < length; i++){
var result = callback.call(undefined, i, obj[i], obj);
if(result === false){
break;
}else if(result === true){
continue;
}
}
}else if(obj.constructor === Object){
for(var i in obj){
if(Object.prototype.hasOwnProperty.call(obj, i)){
var result = callback.call(undefined, i, obj[i], obj);
if(result === false){
break;
}else if(result === true){
continue;
}
}
}
}
}