In-depth understanding of JavaScript – loops come here

Time:2022-11-25

It really hurts to write this because my heart is not here anymore. It took a week of back and forth to pry the keyboard

circular assembly order

There are various loops in JavaScript, such as for, for(reverse), for…in, for…of, forEach, map, etc. What are the characteristics of each of these loops?

for loop

This is the most common loop

for (var i = 0; i < 10; i++) {
    console.log(i)
}

Its characteristic is the most common, the disadvantage is poor readability

for(reverse) loop

As the name suggests, it’s a flipped version of the for loop

for (var i = 10; i > 0; i--) {
    console.log(i)
}

same poor readability

for…in

The for loop can iterate an object‘s enumerable properties other than Symbol in any order, including inherited enumerable properties

It has two characteristics, one is that iterates objects, and the other is that the loop target is enumerable properties, including inherited properties

// Example 1: Iterate objects
var obj = {a: 1, b: 2, c: 3}
for (var key in obj) {
    console.log("obj." + key + " = " + obj[key])
}
// obj.a = 1
// obj.b = 2
// obj.c = 3
// Example 2: Iterating over enumerable properties
Object.prototype.sayHello = function() { console.log('hello') }

// Arrays also belong to objects, and arrays can also use object methods
const iterable = [1, 2, 3]
iterable.name = 'hello'

for (let key in iterable) {
    console.log("key&value: " + key + " = " + iterable[key]) 
}
// key&value: 0 = 1
// key&value: 1 = 2
// key&value: 2 = 3
// key&value: name = hello
// key&value: sayHello = function() { console.log('hello') }

Its usage scenario is when you need to check whether any key in it is a certain value

we are atcopy secretA simple deep copy was written by hand:

function deepClone(source) {
    if (typeof source !== 'object' || source === null) {
        return source
    }
    let target = Array.isArray(source) ? [] : {}
    for (let prop in target) {
        if (source.hasOwnProperty(prop)) {
            target[prop] = typeof source[prop] === 'object' ? deepClone(source[prop]) : source[prop]
        }
    }
    return target
}

Traverse all enumerable properties through for…in, and then filter properties that are not their own properties through hasOwnProperty, so as to get all self-enumerable properties and complete deep copy

Corresponding to it is for…of

for…of

for…in is a new API developed by ES5 to solve the key of traversing objects, while for…of is a feature supported by ES6. Its purpose is to traverse iterable objects (including Array, Map, Set, String, arguments, etc.)

Take example 2 in for…in as an example:

Object.prototype.sayHello = function() { console.log('hello') }
const iterable = [1, 2, 3]
iterable.name = 'hello'

for (let i of iterable) {
  console.log(i); // 3, 5, 7
}

If for…in is to get the key of the object (because the value can be obtained in the for loop), then for…of is more convenient to get the value of the object

forEach

The API added to the array in ES5 can execute a given function once for each element of the array. When traversing, the loop cannot be terminated early by break or return

First look at its parameters, a total of three points:

  • element: the current element
  • index: the index of the current element
  • array: original array
const array1 = ['a', 'b', 'c'];

array1.forEach((str, i, origin) => {
     console.log(`${i}: ${str} / ${origin}`);
});
// 0: a / a,b,c
// 1: b / a,b,c
// 2: c / a,b,c

When called, the original array will not be changed(emphasis)

const array1 = ['a', 'b', 'c'];
array1.forEach((element) => {
    element = element + 1
});
console.log(array1) // [a, b, c]

However, such examples are often encountered during project development:

const arr = [{
    name: 'johan',
    age: 29
}, {
    name: 'elaine',
    age: 29
}]
arr.forEach(ele => {
    if (ele.name === 'johan') {
        ele.age = 22
    }
})
console.log(arr) // [{name: 'johan', age: 22}, {name: 'elaine', age: 29}]

The original array has been changed, why?

Because although calling forEach will not change the original array, the element (current object) may be changed in callbackFn

In the first example, because the current element is a basic type, it is invalid to assign a value to it as an original array. When the current element is a reference type, the situation is different. The reference type exists in the heap memory and shares a reference address , when the pointer is changed, the original array also changes, you can reviewcopy secret

map

The API was added in ES6. This method creates a new array. This new array consists of the values ​​returned by calling the provided function once for each element in the original array

const array1 = [1, 4, 9, 16];

const map1 = array1.map(x => x * 2);

console.log(map1); // [2, 8, 18, 32]

If for…in is often compared with for…of, then the comparison object of forEach is map

For example, forEach has no return value, but map returns a new array; forEach occasionally changes the original array (when callbackFn changes the current object), and because map returns a new array, it never changes the original array (so in the function map is often used in programming, it is a standard pure function)

Derived an interview question

["1","2","3"].map(parseInt)

Why is the answer to this question[1, NaN, NaN]Woolen cloth?

The syntax of parseInt is parseInt(string, radix)

That is to say

// parseInt(string, radix) => map(parseInt(value, index))
// That is to say, its iterative process is:
// first iteration: index is 0: parseInt("1", 0) // 1
// Second iteration: index is 1: parseInt("2", 1) // NaN
// third iteration: index is 2: parseInt("3", 2) // NaN

Radix represents the base number of the base system, the range is 2~36, and the common ones are binary, octal, decimal, and hexadecimal

When binary, except for “0, 1”, other numbers are not valid binary digits

speed comparison

Some diaosi interview questions will ask about the speed of these loop methods. It’s too funny, and it’s not about making rockets. What’s the purpose of asking these?

However, I have written it here, so I might as well test it

example:

const million = 10000000;
const arr = Array(million);
console.time('timer')

for (let i = 0; i < arr.length; i++) {} // for
for (let i = arr.length; i > 0; i--) {}  // for(reverse)
for (const v in arr) {} // for...in
for (const v of arr) {} // for...of
arr.forEach(v => v) // forEach
arr.map(v => v) // map
console.timeEnd('timer')

derivative

Someone asked in Zhihu:How to visually explain the difference between map, foreach, and reduce in JavaScriptYou YuxiThere is a clever metaphor:

  • forEach is what you want them to do in order
  • map is that you take the box and ask them to throw their wallets in, and return a new array at the end, which contains everyone’s wallets
  • reduce is to hold the wallet, each check, add you and the previous synthesis, and calculate the total amount
  • filter is to filter money less than 100 fast, and return a new array at the end, which is full of money greater than 100 fast

Summarize

There are three tricks in the cycle, one is to compare for…in and for…of; the other is to compare forEach and map; the third is to compare other commonly used APIs in ES6

Can’t write anymore, destroy it

References

series of articles