Break v. some, every

I prefer to use forEach instead of the for loop. But sometimes you find yourself implementing a break. At this point, you usually have to switch back to the for loop. For example, the following logic is encountered:

let arr = [1.2.3.4.5]
let text = ' '
for (let v of arr) {
  if (v === 3) { 
    break
  }
  text += v + ', '
}
console.log(text) / / 1, 2, ""
Copy the code

Since the implementation logic of some is inherently short-circuited, i.e. subsequent iterations are no longer executed once true is returned, why not replace forEach with some?

let arr = [1.2.3.4.5]
let text = ' '
arr.some(v= > {
  if (v === 3) { 
    return true
  }
  text += v + ', '
})
console.log(text) / / 1, 2, ""
Copy the code

Normally, we use some to return the result. The fact that it doesn’t play with the return value is a sign that the code is reading: it’s simply using the loop. Of course, it’s not very readable. But it’s a way of replacing for.

Similarly, every is short circuited and can also be used as a substitute for break. But make sure the iteration before break returns true:

let arr = [1.2.3.4.5]
let text = ' '
arr.every(v= > {
  if (v === 3) { 
    return
  }
  text += v + ', '
  return true
})
console.log(text) / / 1, 2, ""
Copy the code

After I posted this article on wechat, my friends said it looked less “pure”. It is recommended to use reduce for implementation, regardless of performance, after all, empty iteration does not waste much performance. If you want to replace the break logic, you must implement some flags, such as:

let arr = [1.2.3.4.5]
let text = arr.reduce(function(p, c) {
  if (this.break) {
    return p
  }
  // ...
  if (c === 3) {
    this.break = true
    return p
  }
  return p + c + ', '
}.bind({}), ' ')
console.log(text) / / 1, 2, ""
Copy the code

As for “pure”, if encapsulated as a function, it is pure:

function formatter(arr) {
  let text = ' '
  arr.some(v= > {
    if (v === 3) { 
      return true
    }
    text += v + ', '
  })
  return text
}
let arr = [1.2.3.4.5]
console.log(formatter(arr)) / / 1, 2, ""
Copy the code

There are also comments, you can do it recursively. The for loop itself is replaceable recursively. And the general break condition, it happens to be a recursive exit. For example, the recursive implementation of this example:

function formatter(arr, text = ' ', i = 0) {
  if (arr.length == 0 || arr[i] == '3') {
    return text
  }
  text += arr[i] + ', '
  return formatter(arr, text, ++i)
}
let arr = [1.2.3.4.5]
console.log(formatter(arr)) / / 1, 2, ""
Copy the code

Continue with the return

As for continue…

let arr = [1.2.3.4.5]
let text = ' '
for (let v of arr) {
  if (v === 3) { 
    continue
  }
  text += v + ', '
}
console.log(text) / / "1,2,4,5."
Copy the code

ForEach would have just returned:

let arr = [1.2.3.4.5]
let text = ' '
arr.forEach(v= > {
  if (v === 3) { 
    return
  }
  text += v + ', '
})
console.log(text) / / "1,2,4,5."
Copy the code

What if both continue and break exist? Such as:

let arr = [1.2.3.4.5]
let text = ' '
for (let v of arr) {
  if (v === 2) {
    continue
  }
  if (v === 4) { 
    break
  }
  text += v + ', '
}
console.log(text) 1, 3, "/ /"
Copy the code

I’ll just use some, because the array apis are essentially for loops.

let arr = [1.2.3.4.5]
let text = ' '
arr.some(v= > {
  if (v === 2) {
    return
  }
  if (v === 4) { 
    return true
  }
  text += v + ', '
})
console.log(text) 1, 3, "/ /"
Copy the code

Some and every need to be noticed

The some function is used to determine whether at least one element in an array satisfies the conditions of the callback function. The callback function can then be called a predicate function. Some is implemented like this in the specification documentation:

Click on
  1. Let O be the result of calling ToObject passing the this value as the argument.
  2. Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".
  3. Let len be ToUint32(lenValue).
  4. If IsCallable(callbackfn) is false, throw a TypeError exception.
  5. If thisArg was supplied, let T be thisArg; else let T be undefined.
  6. Let k be 0.
  7. Repeat, while k < len
    1. Let Pk be ToString(k).
    2. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
    3. If kPresent is true, then
      1. Let kValue be the result of calling the [[Get]] internal method of O with argument Pk.
      2. Let testResult be the result of calling the [[Call]] internal method of callbackfn with T as the this value and argument list containing kValue, k, and O.
      3. If ToBoolean(testResult) is true, return true.
    4. Increase k by 1.
  8. Return false.

With JS simulation, its core logic is as follows:

Array.prototype.some = function(callbackfn, thisArg) {
  let len = Number(this.length)
  let k = 0;
  while(k < len) {
    let Pk = String(k)
    if (Pk in this) {
      let kValue = this[Pk]
      if (callbackfn.call(thisArg, kValue, k, this)) {
        return true
      }
    }
    k++
  }
  return false
}
Copy the code

As you can see, if the callback returns true, the function simply returns and terminates. This is a short-circuit algorithm, not all callbacks are executed once and then all sums are evaluated at the end. Every is similar, but in contrast, when a callback returns false, the whole thing returns false.

In terms of semantics, some is saying: there is a success, I will succeed, and every is saying: there is a failure, I will fail.

It is also important to note that for a sparse array, the callback function is not executed if the index value does not exist. For example, in the following example, the callback function is executed only three times (other apis are similar).

let arr = [1.2.3]
delete arr[1]
arr[5] = 6
console.log("1" in arr) // false
console.log(arr) // [1, empty, 3, empty × 2, 6]
arr.some(v= > {
  console.log(v) / / 1 3 6
})
Copy the code

So an empty array, no matter how the callback function is written, still results in false:

[].some(_= > true) // false
Copy the code

In this paper, to the end.