Home >Web Front-end >JS Tutorial >A detailed introduction to the for loop in JavaScript

A detailed introduction to the for loop in JavaScript

黄舟
黄舟Original
2017-03-03 14:42:401108browse

In ECMAScript5 (ES5 for short), there are three kinds of for loops, namely:

  • Simple for loop

  • for-in

  • forEach

In ECMAScript6 (ES6 for short) released in June 2015, a new loop was added , is:

  • for-of

Let’s take a look at these 4 types of for loops.

Simple for loop

Let’s take a look at the most common way of writing:

const arr = [1, 2, 3];
for(let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

When the length of the array will not change during the loop, we should The length of the array is stored in a variable, which will achieve better efficiency. The following is an improved way of writing:

const arr = [1, 2, 3];
for(let i = 0, len = arr.length; i < len; i++) {
    console.log(arr[i]);
}

for-in

Normally, we can use for-in to traverse it The content of the array, the code is as follows:

const arr = [1, 2, 3];
let index;
for(index in arr) {
    console.log("arr[" + index + "] = " + arr[index]);
}

Under normal circumstances, the running result is as follows:

arr[0] = 1
arr[1] = 2
arr[2] = 3

But this often causes problems.

The truth about for-in

for-in loop traverses the properties of the object, not the index of the array. Therefore, the objects traversed by for-in are not limited to arrays, but can also traverse objects. Examples are as follows:

const person = {
    fname: "san",
    lname: "zhang",
    age: 99
};
let info;
for(info in person) {
    console.log("person[" + info + "] = " + person[info]);
}

The results are as follows:

person[fname] = san
person[lname] = zhang
person[age] = 99

It should be noted that the order in which for-in traverses the attributes is not determined, that is, the order of the output results has nothing to do with the order of the attributes in the object. It also has nothing to do with the alphabetical order of the properties, or any other order.

The truth about Array

Array is an object in Javascript, and the index of Array is the property name. In fact, "array" in Javascript is somewhat misleading. Array in Javascript is not like arrays in most other languages. First of all, Array in Javascript is not continuous in memory. Secondly, the index of Array does not refer to the offset. In fact, the index of Array is not of Number type, but of String type. The reason why we can use arr[0] correctly is that the language can automatically convert 0 of Number type into "0" of String type. Therefore, there are never Array indexes in Javascript, but only properties like "0", "1", etc. Interestingly, every Array object has a length property, causing it to behave more like arrays in other languages. But why is the length attribute not output when traversing the Array object? That's because for-in can only iterate over "enumerable properties", length is a non-enumerable property, and in fact, Array objects have many other non-enumerable properties.

Now, let’s go back and look at the example of using for-in to loop an array. Let’s modify the previous example of traversing the array:

const arr = [1, 2, 3];
arr.name = "Hello world";
let index;
for(index in arr) {
    console.log("arr[" + index + "] = " + arr[index]);
}

The running result is:

arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[name] = Hello world

We see that for-in loops through our new "name" attribute, because for-in traverses all attributes of the object, not just the "index". At the same time, it should be noted that the index values ​​output here, that is, "0", "1", and "2" are not of Number type, but of String type, because they are output as attributes, not indexes. Does that mean that we can just output the contents of the array without adding new properties to our Array object? the answer is negative. Because for-in not only traverses the properties of the array itself, it also traverses all enumerable properties on the array prototype chain. Let's look at an example below:

Array.prototype.fatherName = "Father";
const arr = [1, 2, 3];
arr.name = "Hello world";
let index;
for(index in arr) {
    console.log("arr[" + index + "] = " + arr[index]);
}

The running result is:

arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[name] = Hello world
arr[fatherName] = Father

Writing this, we can find that for-in is not suitable for traversing elements in Array, but is more suitable for traversing objects. attributes in it, which is also the original intention of its creation. There is one exception, that is, sparse arrays. Consider the following example:

let key;
const arr = [];
arr[0] = "a";
arr[100] = "b";
arr[10000] = "c";
for(key in arr) {
    if(arr.hasOwnProperty(key)  &&    
        /^0$|^[1-9]\d*$/.test(key) &&    
        key <= 4294967294               
        ) {
        console.log(arr[key]);
    }
}

for-in will only traverse existing entities. In the above example, for-in traverses 3 times (the traversal attributes are "0", "100", and "10000" elements, a normal for loop will traverse 10001 times). So, as long as it's done properly, for-in can also play a huge role in traversing elements in an Array.

In order to avoid duplication of work, we can wrap the above code:

function arrayHasOwnIndex(array, prop) {
    return array.hasOwnProperty(prop) && 
        /^0$|^[1-9]\d*$/.test(prop) && 
        prop <= 4294967294; // 2^32 - 2
}

Usage examples are as follows:

for (let key in arr) {
    if (arrayHasOwnIndex(arr, key)) {
        console.log(arr[key]);
    }
}

for-in performance

As mentioned above, each iteration operation will also search for instance or prototype properties. Each iteration of the for-in loop will generate more overhead, so it is slower than other loop types. The general speed is 1 of other types of loops. /7. Therefore, you should avoid using for-in loops unless you clearly need to iterate over an object with an unknown number of properties. If you need to traverse a limited number of known attribute lists, it will be faster to use other loops, such as the following example:

const obj = {
    "prop1": "value1",
    "prop2": "value2"
};

const props = ["prop1", "prop2"];
for(let i = 0; i < props.length; i++) {
    console.log(obj[props[i]]);
}

In the above code, the attributes of the object are stored in an array, compared to for- in searches for each attribute, this code only focuses on the given attribute, saving the overhead and time of the loop.

forEach

In ES5, a new loop is introduced, the forEach loop.

const arr = [1, 2, 3];
arr.forEach((data) => {
    console.log(data);
});

Run results:

1
2
3

The forEach method executes a callback function for each item in the array that contains a valid value, those items that have been deleted (using the delete method, etc.) or have never been assigned a value Will be skipped (except items whose values ​​are undefined or null). The callback function will be passed in three parameters in sequence:

  • 数组当前项的值;

  • 数组当前项的索引;

  • 数组对象本身;

需要注意的是,forEach 遍历的范围在第一次调用 callback 前就会确定。调用forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。

const arr = [];
arr[0] = "a";
arr[3] = "b";
arr[10] = "c";
arr.name = "Hello world";
arr.forEach((data, index, array) => {
    console.log(data, index, array);
});

运行结果:

a 0 ["a", 3: "b", 10: "c", name: "Hello world"]
b 3 ["a", 3: "b", 10: "c", name: "Hello world"]
c 10 ["a", 3: "b", 10: "c", name: "Hello world"]

这里的 index 是 Number 类型,并且也不会像 for-in 一样遍历原型链上的属性。

所以,使用 forEach 时,我们不需要专门地声明 index 和遍历的元素,因为这些都作为回调函数的参数。

另外,forEach 将会遍历数组中的所有元素,但是 ES5 定义了一些其他有用的方法,下面是一部分:

  • every: 循环在第一次 return false 后返回

  • some: 循环在第一次 return true 后返回

  • filter: 返回一个新的数组,该数组内的元素满足回调函数

  • map: 将原数组中的元素处理后再返回

  • reduce: 对数组中的元素依次处理,将上次处理结果作为下次处理的输入,最后得到最终结果。

forEach 性能

首先感谢@papa pa的提醒,才发现我之前的理解有错误。

大家可以看 jsPerf ,在不同浏览器下测试的结果都是 forEach 的速度不如 for。如果大家把测试代码放在控制台的话,可能会得到不一样的结果,主要原因是控制台的执行环境与真实的代码执行环境有所区别。

for-of

先来看个例子:

const arr = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];
for(let data of arr) {
    console.log(data);
}

运行结果是:

a
b
c

为什么要引进 for-of?

要回答这个问题,我们先来看看ES6之前的 3 种 for 循环有什么缺陷:

  • forEach 不能 break 和 return;

  • for-in 缺点更加明显,它不仅遍历数组中的元素,还会遍历自定义的属性,甚至原型链上的属性都被访问到。而且,遍历数组元素的顺序可能是随机的。

所以,鉴于以上种种缺陷,我们需要改进原先的 for 循环。但 ES6 不会破坏你已经写好的 JS 代码。目前,成千上万的 Web 网站依赖 for-in 循环,其中一些网站甚至将其用于数组遍历。如果想通过修正 for-in 循环增加数组遍历支持会让这一切变得更加混乱,因此,标准委员会在 ES6 中增加了一种新的循环语法来解决目前的问题,即 for-of 。

那 for-of 到底可以干什么呢?

  • 跟 forEach 相比,可以正确响应 break, continue, return。

  • for-of 循环不仅支持数组,还支持大多数类数组对象,例如 DOM nodelist 对象。

  • for-of 循环也支持字符串遍历,它将字符串视为一系列 Unicode 字符来进行遍历。

  • for-of 也支持 Map 和 Set (两者均为 ES6 中新增的类型)对象遍历。

总结一下,for-of 循环有以下几个特征:

  • 这是最简洁、最直接的遍历数组元素的语法。

  • 这个方法避开了 for-in 循环的所有缺陷。

  • 与 forEach 不同的是,它可以正确响应 break、continue 和 return 语句。

  • 其不仅可以遍历数组,还可以遍历类数组对象和其他可迭代对象。

但需要注意的是,for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用
for-in 循环(这也是它的本职工作)。

最后要说的是,ES6 引进的另一个方式也能实现遍历数组的值,那就是 Iterator。上个例子:

const arr = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];
const iter = arr[Symbol.iterator]();

iter.next() // { value: &#39;a&#39;, done: false }
iter.next() // { value: &#39;b&#39;, done: false }
iter.next() // { value: &#39;c&#39;, done: false }
iter.next() // { value: undefined, done: true }

不过,这个内容超出了本文的范围,而且 Iterator 要讲的也有很多,以后有时间专门写一篇文章介绍,欢迎关注。

 以上就是深入了解 JavaScript 中的 for 循环 的详细介绍 的内容,更多相关内容请关注PHP中文网(www.php.cn)!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn