[] .forEach.call ()은 JavaScript에서 무엇을합니까?


135

몇 가지 코드 조각을보고 있었고 빈 배열에 forEach가 적용된 노드 목록에서 함수를 호출하는 여러 요소를 발견했습니다.

예를 들어 다음과 같은 것이 있습니다.

[].forEach.call( document.querySelectorAll('a'), function(el) {
   // whatever with the current node
});

그러나 그것이 어떻게 작동하는지 이해할 수 없습니다. 누구든지 forEach 앞에서 빈 배열의 동작과 작동 방식을 설명 할 수 있습니까 call?

답변:


203

[]배열입니다.
이 배열은 전혀 사용되지 않습니다.

배열을 사용하면과 같은 배열 프로토 타입에 액세스 할 수 있으므로 페이지에 표시 .forEach됩니다.

입력하는 것보다 빠릅니다. Array.prototype.forEach.call(...);

다음으로, forEach기능을 입력으로받는 기능입니다.

[1,2,3].forEach(function (num) { console.log(num); });

...의 각 요소에 대해 this( this어레이가 있고 배열이 있고 length그 부분에 액세스 할 수 있다는 점에서 this[1])은 세 가지를 전달합니다.

  1. 배열 내의 요소
  2. 요소의 인덱스 (세번째 요소는 통과합니다 2)
  3. 배열에 대한 참조

마지막으로, .call함수가 가진 프로토 타입입니다 (다른 함수에서 호출되는 함수입니다).
.call첫 번째 인자를 가지고 대체합니다 this당신이 통과 무엇 이건 정규 함수의 내부에 call첫 번째 인수로, ( undefined또는 null사용 window일상 JS에서, 또는 당신이 경우 "엄격 모드"에서 통과 무엇이든 될 것입니다). 나머지 인수는 원래 함수로 전달됩니다.

[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) {
    console.log(i + ": " + item);
});
// 0: "a"
// 1: "b"
// 2: "c"

따라서 forEach함수 를 호출하는 빠른 방법을 만들고 this빈 배열에서 모든 <a>태그 목록으로 변경 하고 각 <a>순서에 따라 제공된 함수를 호출합니다.

편집하다

논리적 결론 / 정리

아래에는 함수형 프로그래밍 시도를 폐기하고 매번 수동 인라인 루핑을 고수하는 기사에 대한 링크가 있습니다.

내가 그 동안 말하고 싶지만 .forEach그 대응보다 도움이된다, .map(transformer), .filter(predicate), .reduce(combiner, initialValue)중 하나에 접근하면서 당신이 정말로 원하는 모두가 외부 세계 (안 배열), N-시간을 수정할 때, 그것은 여전히 용도로 사용 arr[i]또는 i.

Motto는 분명히 재능 있고 지식이 풍부한 사람이므로 불일치를 처리하는 방법은 무엇이며, 내가하고있는 일 / 내가 가고있는 곳을 알고 있다고 상상하고 싶습니다 ... 번은 머리 우선 학습)?

대답은 실제로 매우 간단하며 밥 삼촌과 크록 포드 경은 감시로 인해 둘 다 직면 할 것입니다.

그것을 청소하십시오 .

function toArray (arrLike) { // or asArray(), or array(), or *whatever*
  return [].slice.call(arrLike);
}

var checked = toArray(checkboxes).filter(isChecked);
checked.forEach(listValues);

자, 당신이 이것을 해야하는지 여부에 대해 의문을 가지고 있다면, 대답은 아마 아닐
것입니다 ... 이 정확한 일은 요즘 고급 기능을 가진 ... 모든 라이브러리에 의해 이루어집니다.
lodash, 밑줄 또는 jQuery를 사용하는 경우 모두 요소 집합을 취하고 n 번 동작을 수행하는 방법이 있습니다.
그런 것을 사용하지 않는다면, 반드시 자신의 것을 쓰십시오.

lib.array = (arrLike, start, end) => [].slice.call(arrLike, start, end);
lib.extend = function (subject) {
  var others = lib.array(arguments, 1);
  return others.reduce(appendKeys, subject);
};

ES6 (ES2015) 이상 업데이트

뿐만 아니라입니다 slice( )/ array( )/ 등 도우미 방법은 (그들이 정상적으로)가 배열을 사용하는 것처럼 목록을 사용하기 원하는 사람들을위한 인생을 더 쉽게 만들려고하지만, 상대적으로 근처의 ES6 + 브라우저에서 동작의 고급 스러움을 가지고있는 사람들을위한 Babel에서 미래 또는 "번역"의 언어 기능이 내장되어있어 이러한 유형의 작업이 불필요합니다.

function countArgs (...allArgs) {
  return allArgs.length;
}

function logArgs (...allArgs) {
  return allArgs.forEach(arg => console.log(arg));
}

function extend (subject, ...others) { /* return ... */ }


var nodeArray = [ ...nodeList1, ...nodeList2 ];

매우 깨끗하고 매우 유용합니다. Rest and Spread 연산자를
찾으십시오 . BabelJS 사이트에서 사용해보십시오. 기술 스택이 올바른 경우 Babel 및 빌드 단계와 함께 프로덕션 환경에서 사용하십시오.


(가) 배열에 배열이 아닌에서 변환 사용할 수하지 않는 좋은 이유가 없습니다 ... 그냥 ... 아무것도하지 않는 코드의 혼란을하지 않습니다 하지만 모든 곳에서, 같은 추한 줄 것을 붙여 넣기.


이제 내가 한 경우 약간 혼란스러운 원인입니다. console.log (this); 나는 항상 창 객체를 얻을 것이다. 그리고 나는 .call이 이것의 가치를 바꾼다고 생각했다
Muhammad Saleh

.call 않는 값 변경 this사용하고있는 이유는, callforeach 문으로합니다. foreach는 모습이 좋아하는 경우에 forEach (fn) { var i = 0; var len = this.length; for (; i < length; i += 1) { fn( this[i], i, this ); }변경하여 다음 this말함으로써 forEach.call( newArrLike )이 같은 그 새로운 객체를 사용하는 것을 의미 this.
Norguard

2
@MuhammadSaleh은 .call의 값은 변경되지 않습니다 this당신이에 전달 내부 forEach, .call의 값 변경 this 대해 forEach의 내부 . 왜 호출하려는 함수로 this전달되지 않는지에 대해 혼란 스러우면 JavaScript forEach에서 동작을 찾아야합니다 this. 부록, 여기에만 적용 (및지도 / 필터 / 감소)으로 행 번째 인수가 forEach어느 세트 this함수 내부 arr.forEach(fn, thisInFn)[].forEach.call(arrLike, fn, thisInFn);또는 사용 .bind; arr.forEach(fn.bind(thisInFn));
Norguard

1
@thetrystero 그것이 바로 요점입니다. []그래서 당신이 할 수있는 것을, 배열로 단지가 방법 및 변경 이 작업을 수행 할 설정합니다. 다음과 같습니다 세트 내부의 검은 마법있을 것을 제외하고 내부 당신이로 전달 된 것 같음이 . .call.forEachthis.callfunction (thisArg, a, b, c) { var method = this; method(a, b, c); }thismethodthisArg
Norguard

1
간단히 말해 ... Array.from ()?
zakius

53

querySelectorAll메서드NodeList배열과 비슷한을 반환 하지만 배열은 아닙니다. 따라서 forEach메소드를 가지고 있지 않습니다 (어레이 객체는를 통해 상속받습니다 Array.prototype).

a NodeList는 배열과 비슷하기 때문에 실제로 배열 메소드가 작동 [].forEach.call하므로을 사용 하면 마치 할 수있는 것처럼 Array.prototype.forEach메소드를 호출합니다 .NodeListyourNodeList.forEach(/*...*/)

빈 배열 리터럴은 확장 버전의 바로 가기 일뿐입니다.

Array.prototype.forEach.call(/*...*/);

7
분명히 말하면, 자체 프로토 타입 이없는 어레이와 유사한 구조를 반복하려고 할 때 표준 어레이 를 사용 [].forEach.call(['a','b'], cb)하는 ['a','b'].forEach(cb)일상적인 애플리케이션에서 오버 를 사용 하는 데 이점이 forEach없습니까? 그 맞습니까?
Matt Fletcher

2
@MattFletcher : 네 맞습니다. 둘 다 작동하지만 왜 지나치게 복잡합니까? 배열 자체에서 직접 메소드를 호출하십시오.
James Allardice

감사합니다. 그리고 나는 왜 아마 거리의 cred를 위해 ALDI와 그와 같은 노인에게 깊은 인상을 주 었는지 모른다. 나는에 충실하겠습니다 [].forEach():)
매트 플레처에게

var section = document.querySelectorAll(".section"); section.forEach((item)=>{ console.log(item.offsetTop) }) 잘 작동
daslicht

이전 버전의 IE에는 @daslicht가 없습니다! (즉, , 그러나, 지원 forEachNodeList의 객체를 배열에 있지만)
Djave

21

다른 답변은이 코드를 잘 설명 했으므로 제안을 추가 할 것입니다.

이것은 단순성과 명확성을 위해 리팩토링되어야하는 코드의 좋은 예입니다. 사용 [].forEach.call()하거나 Array.prototype.forEach.call()매번 수행 할 때마다 간단한 기능을 사용하십시오.

function forEach( list, callback ) {
    Array.prototype.forEach.call( list, callback );
}

이제 더 복잡하고 모호한 코드 대신이 함수를 호출 할 수 있습니다.

forEach( document.querySelectorAll('a'), function( el ) {
   // whatever with the current node
});

5

사용하여 더 잘 쓸 수 있습니다

Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) {

});

기능 document.querySelectorAll('a')은 배열과 유사한 객체를 반환하지만 Array유형 에서 상속하지는 않습니다 . 그래서 우리 forEachArray.prototype객체 에서 메소드를 호출합니다.document.querySelectorAll('a')


4
[].forEach.call( document.querySelectorAll('a'), function(el) {
   // whatever with the current node
});

기본적으로 다음과 같습니다.

var arr = document.querySelectorAll('a');
arr.forEach(function(el) {
   // whatever with the current node
});

2

이 오래된 질문을 업데이트하고 싶습니다.

[].foreach.call()최신 브라우저에서 요소를 반복하는 데 사용 하는 이유 는 대부분 끝났습니다. document.querySelectorAll("a").foreach()직접 사용할 수 있습니다.

NodeList 객체는 일반적으로 Node.childNodes와 같은 속성 및 document.querySelectorAll ()과 같은 메서드에 의해 반환되는 노드 모음입니다.

NodeList는 배열이 아니지만 forEach ()를 사용하여 반복 할 수 있습니다 . Array.from ()을 사용하여 실제 Array로 변환 할 수도 있습니다.

그러나 일부 구형 브라우저는 NodeList.forEach () 또는 Array.from ()을 구현하지 않았습니다. 이는 Array.prototype.forEach ()를 사용하여 회피 할 수 있습니다.이 문서의 예제를 참조하십시오.


1

빈 배열 forEach의 프로토 타입에는 Function 객체 인 속성 이 있습니다. (빈 배열은 forEach모든 Array객체가 가진 함수에 대한 참조를 얻는 쉬운 방법 일뿐 입니다.) 함수 객체는 차례로 call함수이기도합니다. 함수의 call함수 를 호출 하면 주어진 인수로 함수를 실행합니다. 첫 번째 인수는 this호출 된 함수에 있습니다.

call기능에 대한 설명서는 여기에서 찾을 수 있습니다 . 에 대한 설명서 forEach여기에 있습니다 .


1

한 줄만 추가하십시오.

NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;

그리고 짜잔!

document.querySelectorAll('a').forEach(function(el) {
  // whatever with the current node
});

즐겨 :-)

경고 : NodeList는 글로벌 클래스입니다. 공공 도서관을 작성하는 경우이 권장 사항을 사용하지 마십시오. 그러나 웹 사이트 또는 node.js 앱에서 작업 할 때 자기 효능을 높이는 매우 편리한 방법입니다.


1

항상 빠르고 더러운 솔루션을 사용합니다. 모범 사례만큼 프로토 타입을 건드리지 않습니다. 물론이를 개선하는 데는 여러 가지 방법이 있지만 아이디어를 얻을 수 있습니다.

const forEach = (array, callback) => {
  if (!array || !array.length || !callback) return
  for (var i = 0; i < array.length; i++) {
    callback(array[i], i);
  }
}

forEach(document.querySelectorAll('.a-class'), (item, index) => {
  console.log(`Item: ${item}, index: ${index}`);
});

0

[]항상 새 배열 new Array()을 반환하지만 Array, 사용자가 덮어 쓸 수는 있지만 배열을 반환 할 수 는 있지만 보장 []할 수는 없습니다. 따라서 이것은의 프로토 타입을 얻는 안전한 방법이며 Array, 설명 된대로 callarraylike nodelist (this)에서 함수를 실행하는 데 사용됩니다.

주어진이 값과 개별적으로 제공된 인수로 함수를 호출합니다. mdn


동안 []사실 항상 배열을 반환 , window.Array모든 오래된 브라우저 window.Array.prototype.forEach에서 무엇이든 덮어 쓸 수 있으므로 무엇이든 덮어 쓸 수 있습니다. 게터 / 세터를 지원하지 않는 브라우저 나 객체 밀봉 / 고정 (고정이 자체 확장 성 문제의 원인)을 지원하지 않는 브라우저의 경우 안전을 보장 할 수 없습니다.
Norguard

덮어 쓰기 가능성 prototype이나 방법 에 대한 추가 정보를 추가하려면 답변을 편집하십시오 forEach.
Xotic750

0

NorguardWHAT [].forEach.call() 가 수행하는 작업 과 James Allardice 가 수행하는 이유를 설명 했습니다. querySelectorAll은 NodeListforEach 메소드가 없는를 반환하기 때문에 ...

Chrome 51 이상, Firefox 50 이상, Opera 38, Safari 10과 같은 최신 브라우저가 없다면

그렇지 않은 경우 Polyfill을 추가 할 수 있습니다 .

if (window.NodeList && !NodeList.prototype.forEach) {
    NodeList.prototype.forEach = function (callback, thisArg) {
        thisArg = thisArg || window;
        for (var i = 0; i < this.length; i++) {
            callback.call(thisArg, this[i], i, this);
        }
    };
}

0

이 페이지에 대한 많은 좋은 정보 ( 답변 + + 댓글 참조) 있지만 ) 최근 OP와 동일한 질문이 있었으며 전체 그림을 얻는 데 약간의 파기가 필요했습니다. 여기 짧은 버전이 있습니다 :

목표는 Array배열과 같은 메소드 를 사용 하는 것입니다.NodeList 그러한 메소드 자체가없는 하는 것입니다.

더 오래된 패턴은를 통해 Array의 메소드를 선택했으며 Function.call(), 입력하기가 더 짧기 []보다는 배열 리터럴 ( )을 사용했습니다 Array.prototype.

[].forEach.call(document.querySelectorAll('a'), a => {})

최신 패턴 (ECMAScript 2015 이후)은 다음을 사용합니다 Array.from().

Array.from(document.querySelectorAll('a')).forEach(a => {})
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.