JavaScript some과 every 차이
JavaScript 배열이 조건을 만족하는지 확인할 때 some()과 every()를 사용할 수 있다.
두 메서드 모두 boolean을 반환하지만 통과해야 하는 요소의 수가 다르다.
하나라도 조건을 만족하면 되는 경우에는 some()을 사용한다.
모든 요소가 조건을 만족해야 하는 경우에는 every()를 사용한다.
some과 every의 반환 기준
some()은 콜백이 truthy를 반환하는 요소를 하나라도 찾으면 true를 반환한다.
목록에 특정 요소가 존재하는지, 하나 이상의 오류가 있는지 확인하는 조건에 맞는다.
every()는 방문한 모든 요소에서 콜백이 truthy를 반환해야 true를 반환한다.
모든 입력값이 유효한지, 전체 작업이 완료됐는지 확인하는 조건에 맞는다.
const users = [
{ id: 1, role: 'member', verified: true },
{ id: 2, role: 'admin', verified: true },
{ id: 3, role: 'member', verified: false },
]
const hasAdmin = users.some((user) => user.role === 'admin')
const allVerified = users.every((user) => user.verified)
console.log(hasAdmin) // true
console.log(allVerified) // false두 메서드 모두 새 배열이나 요소가 아닌 boolean을 반환한다.
반환값과 단축 평가
some()은 첫 truthy 결과를 만나는 즉시 true를 반환하고 순회를 끝낸다.every()는 첫 falsy 결과를 만나는 즉시 false를 반환하고 순회를 끝낸다.
const numbers = [2, 4, 7, 10]
let someCalls = 0
let everyCalls = 0
const hasLargeNumber = numbers.some((number) => {
someCalls += 1
return number > 5
})
const allSmallNumbers = numbers.every((number) => {
everyCalls += 1
return number < 5
})
console.log(hasLargeNumber, someCalls) // true, 3
console.log(allSmallNumbers, everyCalls) // false, 3단축 평가는 불필요한 조건 검사를 줄여 준다.
반대로 콜백이 모든 요소에서 반드시 실행될 것이라고 기대하면 일부 작업이 누락된다.
모든 요소에 부수 효과를 실행하려는 목적이라면 some()이나 every()를 순회 도구로 사용하지 않는 편이 좋다.
콜백 반환값
콜백은 현재 요소, index, 순회 중인 배열을 인자로 받고 결과는 boolean으로 변환된다.
빈 문자열과 0은 falsy이고 객체와 Promise는 truthy다.
문자열 자체를 반환하는 코드도 동작하지만 field.error !== ''처럼 판정을 명시하면 의도를 더 쉽게 읽을 수 있다.
빈 배열의 결과
빈 배열에서 some()은 false, every()는 true를 반환한다.
콜백은 두 경우 모두 한 번도 실행되지 않는다.
console.log([].some(() => true)) // false
console.log([].every(() => false)) // true빈 배열에는 조건을 만족하는 요소가 하나도 없으므로 some()의 결과는 false다.
반대로 조건을 위반하는 요소도 하나도 없으므로 모든 요소가 조건을 만족한다는 every()의 결과는 true다.
이 규칙 때문에 입력 목록이 비어 있어도 제출 조건이 통과할 수 있다.
요소가 하나 이상이어야 한다는 요구사항은 배열 길이로 따로 표현해야 한다.
예를 들어 requiredFields.length > 0 && requiredFields.every((field) => field.valid)처럼 길이 조건을 먼저 확인한다.every()가 true라는 사실만으로 검사한 요소가 실제로 존재했다고 판단하면 안 된다.
부정 조건의 표현
일치하는 요소가 하나도 없어야 한다면 !items.some(isBlocked)처럼 some 결과를 부정할 수 있다.
모든 요소가 통과하지는 않는다는 조건은 !items.every(isReady)로 표현할 수 있다.
이중 부정이 이어진다면 isValid, hasInvalid처럼 조건에 이름을 붙이는 편이 낫다.
희소 배열의 빈 슬롯
some()과 every()는 값이 할당되지 않은 빈 슬롯에서 콜백을 호출하지 않는다.
length만 있고 실제 요소가 없는 희소 배열에서는 빈 배열과 같은 결과가 나올 수 있다.
const slots = new Array(3)
console.log(slots.some(() => true)) // false
console.log(slots.every(() => false)) // true직접 넣은 undefined는 빈 슬롯과 다르며 콜백의 방문 대상이다.
희소 배열에 every()를 사용해 데이터가 모두 유효하다고 판단하면 누락된 index를 놓칠 수 있다.
두 메서드는 첫 콜백 전에 순회할 length 범위를 정하므로 도중에 추가한 요소는 방문하지 않는다.
아직 방문하지 않은 요소를 삭제하면 해당 index도 건너뛴다.
검사 중 같은 배열을 변경하지 않고 빈 슬롯이 없는 배열을 사용하는 편이 결과를 예측하기 쉽다.
비동기 콜백의 함정
some()과 every()는 async 콜백이 반환한 Promise를 기다리지 않는다.
Promise 객체 자체는 truthy이므로 실제 비동기 결과가 false여도 판정이 끝날 수 있다.
const users = [{ id: 1 }, { id: 2 }]
async function checkAccess() {
return false
}
const hasAccess = users.some(async (user) => {
return await checkAccess(user.id)
})
console.log(hasAccess) // true배열이 비어 있지 않다면 첫 async 콜백이 반환한 Promise만으로 some()은 true가 된다.
같은 이유로 every()도 각 Promise를 truthy로 보고 실제 결과를 검사하지 않는다.
모든 검사를 병렬로 실행해도 된다면 Promise를 먼저 해결한 뒤 boolean 배열에 메서드를 적용한다.
const accessResults = await Promise.all(
users.map((user) => checkAccess(user.id)),
)
const hasAccess = accessResults.some(Boolean)
const allHaveAccess = accessResults.every(Boolean)첫 성공이나 실패에서 이후 비동기 요청을 시작하지 않아야 한다면 for...of와 await로 순차 검사한다.
다른 배열 메서드와의 선택 기준
조건을 만족하는 요소가 존재하는지만 필요하면 some()을 사용한다.
모든 요소가 조건을 만족하는지 필요하면 every()를 사용한다.
첫 요소 자체가 필요하면 find(), 조건을 통과한 요소 목록이 필요하면 filter()가 맞다.
반환 형태에 따른 차이는 JavaScript find와 filter 차이에서 자세히 볼 수 있다.
모든 요소를 변환한 새 배열이 필요하면 map(), 모든 요소에 작업을 실행하고 반환값이 필요하지 않으면 forEach()를 검토한다.
두 메서드의 기준은 JavaScript map과 forEach 차이에서 이어서 확인할 수 있다.
특정 값 하나의 포함 여부만 확인할 때는 predicate를 만들지 않고 includes()로 의도를 드러낼 수 있다.
필요한 결과가 boolean인지, 요소인지, 새 배열인지부터 정하면 배열 메서드 선택이 단순해진다.
참고 자료



















