for
下面是for循环的语法:
for(语句1;语句2;语句3){
被执行的代码块
}
- 语句1:在循环(代码块)开始前执行,通常用来初始化循环中的变量(任意个)。
- 语句2:定义运行循环(代码块)的条件,用来评估初始变量的条件,如果语句 2 返回 true,则循环再次开始,如果返回 false,则循环将结束。
- 语句3:在循环(代码块)已被执行之后执行,用来增加初始变量的值。
这三个语句都是可选的。
以 jquery中.unwrap()的源码分析作为案例(非官方分析,编写的小demo罢了)
function unwrap(element) {
var parentElement = element.parentNode;
var children = parentElement.children;
for (; children.length; ) {
parentElement.parentNode.insertBefore(children[0], parentElement);
}
parentElement.parentNode.removeChild(parentElement);
}
在上述代码中,需要将要去除元素的父元素。做法是将element的子元素用for循环挨个插入到element的父级元素之前,循环完成后就将element的父元素删除。在循环中,不需要初始化循环中的变量和改变循环变量的值,因为在不断的insertBefore操作中,循环条件在不断改变。
查看代码的运行,可以清楚地知道for循环实际上是遍历下标来指向值。因此它更适用于操作循环次数已知且具有数字下标的数据。
(for…in…)
for(var s in obj){//obj为对象
console.log(s,obj[s]);//打印出obj内的属性名和属性值
}
(for…in…)的特征总结如下:
- (for…in…)用来循环对象的所有属性,其实准确的来说,是循环对象的可枚举属性;
- 如果想要获取到循环对象的属性值,则需要手动再获取(如obj[s]),而且对象的属性不一定包含值,它们可能是具备getter/setter的访问描述符;
- (for…in…)可以访问到对象原型链上的数据;
- (for…in…)是为普通对象设计的,你可以遍历得到字符串类型的键
所以为什么不推荐使用(for…in)来遍历数组呢?原因就在于这种枚举不仅会包含所有数值索引还会包含所有可枚举属性,在某些情况下,可能按照随机顺序遍历数组元素。
Array.forEach( )
array.forEach(function(currentValue,index,arr){},thisValue);
- forEach为es6新增的辅助迭代器,作用是对调用者的元素进行遍历并进行相关操作;
- 仅能被数组或者使用document.querySelectorAll获取到的类数组进行调用;
- 第一个参数为回调函数,其接收的参数依次为当前元素(必填),当前元素的索引值(可选),当前对象所属的数组对象;第二个参数可以限定回调函数内this的指向;
- 在回调函数内部无法使用break来跳出循环,也不能使用return来返回外层函数
由它的特性可以推断出forEach的实现代码如下:
Array.prototype.forEach = function(callback, context) {
for (var i=0; i<this.length; i++) {
callback.call(context, this[i], i);
}
};
(for … of …)
先说下(for…of…)的作用,它与(for…in…)不同,它是用来遍历数据,如对象里的属性值。
而说到(for…of…)就必须提到迭代协议和迭代器。因为不是所有的对象都可以被for…of的,只有实现了迭代器(迭代协议)的对象才可以for…of 。
for..of会寻找内置的或者自定义的@@iterator对象并调用它的next()。具体流程如下:
1. 首先调用(for…of…)的对象的属性里必须有一个函数,它的函数名必须为Symbol.iterator;
2. 该函数的返回值必须为一个对象,且对象内包含一个next()函数;
3. next()函数也必须有一个返回值,返回值类型也为一个对象。其包含两个属性:done和value。
4. 程序会根据该对象的done来处理后续行为,如果done为false,则把该对象的value赋值给of前面的变量。否则就会继续调用next,直至done为true再终止。
由它的特性可以推断出(for…of…)的实现代码如下:
obj[Symbol.iterator] = function() {
let keys = Object.keys(obj);
var n = keys.length;
var i = 0;
return {
next() {
if (i < n) {
let key = keys[i];
i++;
return {done: false, value: { obj[key]}
} else {
return {done: true}
}
}
}
};
我们可以来分析下它与其他循环的不同:
1. 与forEach()不同的是,(for…of…)可以正确响应break、continue和return语句;
2. 与(for..in…)相比,除了能够快速的得到属性值外,它还避开了(for…in…)所有的缺陷;
3. (for..of…)循环不仅支持数组,还支持大多数类数组对象,例如DOM NodeList对象;
4. (for..of…)循环也支持字符串遍历,它将字符串视为一系列的Unicode字符来进行遍历;