перерыв и некоторые, каждый
Лично нравится использоватьforEachзаменитьforцикл. Но иногда обнаруживается, что в процессе реализации необходимо использоватьbreak.这时,一般又得切换回forцикл. Например, встретите следующую логику:
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,"
смотреть сегоднястатьядумал, так какsomeСама логика реализации замкнута накоротко, т.е. как только она возвращаетtrueПоследующие итерации больше не будут выполняться, так почему бы не использоватьsomeзаменятьforEachШерстяная ткань?
let arr = [1, 2, 3, 4, 5]
let text = ''
arr.some(v => {
if (v === 3) {
return true
}
text += v + ','
})
console.log(text) // "1,2,"
Как правило, мы используемsomeВсе используют его для возврата результатов. И эта практика не поднимать шума по поводу возвращаемого значения является сигналом чтения кода: оказалось, что это простое использование его цикла. Конечно, этот код не очень читабелен. Но он заменяетforпрочь.
По аналогии,everyОн тоже короткозамкнутый, конечно, его тоже можно заменитьbreak. Но убедитесь,breakпредыдущая итерация возвращает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,"
После того, как эта статья была размещена в кругу друзей, друзья сказали, что она не выглядит такой уж «чистой». Рекомендуется использоватьreduceРеализация вне зависимости от производительности, в конце концов, пустая итерация не тратит много производительности. Если вы хотите заменитьbreakлогика, то вы должны использовать некоторыеflagдобиться, например:
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,"
Кроме того, насчет «чистого», если он инкапсулирован в функцию, то он чистый:
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,"
Есть также пользователи сети, оставляющие сообщение, что можно сделать рекурсивным способом.forСам цикл можно заменить рекурсией. и вообщеbreakУсловие, которое является рекурсивным выходом. Например, рекурсивная реализация этого примера:
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,"
продолжить и вернуться
Что касается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,"
Ответ на удивление прост,forEachнепосредственныйreturnДостаточно:
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,"
еслиcontinueа такжеbreakсуществуют одновременно? Например:
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,"
просто используйтеsomeВ любом случае это хорошо, эти API массива по сути всеforцикл.
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,"
Некоторым и каждому нужно обратить внимание
someФункция используется для определения того, есть ли в массиве хотя бы один элемент, удовлетворяющий условиям функции обратного вызова. В это время функцию обратного вызова можно назвать функцией-предикатом (то есть смысл судить о том, является она или нет).СпецификациясерединаsomeЭто реализовано так:
Нажмите, чтобы развернуть
- Let O be the result of calling ToObject passing the this value as the argument.
- Let lenValue be the result of calling the [[Get]] internal method of O with the argument
"length". - Let len be ToUint32(lenValue).
- If IsCallable(callbackfn) is false, throw a TypeError exception.
- If thisArg was supplied, let T be thisArg; else let T be undefined.
- Let k be 0.
- Repeat, while k < len
- Let Pk be ToString(k).
- Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
- If kPresent is true, then
- Let kValue be the result of calling the [[Get]] internal method of O with argument Pk.
- 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.
- If ToBoolean(testResult) is true, return true.
- Increase k by 1.
- Return false.
При моделировании с помощью JS основная логика выглядит следующим образом:
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
}
Видно, что возвращаемое значение обратного вызова равноtrueЕсли это так, функция возвращается напрямую и завершается. Это алгоритм короткого замыкания, не все обратные вызовы выполняются один раз, а затем окончательно вычисляются все значения AND.everyАналогично, но наоборот, возвращаемое значение обратного вызова равноfalse, целое возвращается напрямуюfalse.
С точки зрения семантики, выраженной в реализации,someговорит:Есть успех, у меня получится,а такжеeveryговорит:Если один терпит неудачу, я терплю неудачу.
Кроме того, следует подчеркнуть, что для разреженных массивов функция обратного вызова не выполняется, когда значение индекса не существует. Например, функция обратного вызова в следующем примере выполняется только 3 раза (аналогично другим API).
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
})
Таким образом, пустой массив, как бы ни была написана функция обратного вызова, результат все равноfalse:
[].some(_ => true) // false
Эта статья закончилась.