जावास्क्रिप्ट में [] .forEach.call () क्या करता है?


135

मैं कोड के कुछ स्निपेट देख रहा था, और मैंने पाया कि कई तत्वों ने एक नोड सूची पर एक फ़ंक्शन को कॉल किया था, जो कि एक खाली सरणी के लिए लागू किया गया था।

उदाहरण के लिए मेरे पास कुछ इस तरह है:

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

लेकिन मैं नहीं समझ सकता कि यह कैसे काम करता है। क्या कोई मुझे फॉरएच के सामने खाली सरणी के व्यवहार की व्याख्या कर सकता है और कैसे 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, या आप जो भी पास करेंगे, अगर "सख्त-मोड" में होगा)। बाकी तर्कों को मूल समारोह में पारित किया जाएगा।

[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), यह अभी भी उद्देश्यों को पूरा करती है जब सभी तुम सच में क्या करना चाहते हैं बाहर की दुनिया (नहीं सरणी), एन-बार संशोधित है, जबकि करने के लिए या तो पहुंच arr[i]या i

इसलिए हम असमानता से कैसे निपटते हैं, जैसा कि आदर्श वाक्य स्पष्ट रूप से एक प्रतिभाशाली और जानकार आदमी है, और मैं कल्पना करना चाहूंगा कि मुझे पता है कि मैं क्या कर रहा हूं / मैं (अब और फिर ...) अन्य यह पहली बार सीखने का समय है)?

जवाब वास्तव में काफी सरल है, और कुछ अंकल बॉब और सर क्रॉकफोर्ड दोनों का सामना करेंगे, ओवरसाइट के कारण:

इसे साफ करो

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

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

अब, यदि आप सवाल कर रहे हैं कि क्या आपको ऐसा करने की आवश्यकता है, तो, इसका उत्तर अच्छी तरह से नहीं हो सकता है ...
यह सटीक काम इन दिनों ... उच्चतर सुविधाओं वाले पुस्तकालय द्वारा किया जाता है।
यदि आप लॉश या अंडरस्कोर या यहां तक ​​कि jQuery का उपयोग कर रहे हैं, तो वे सभी तत्वों का एक सेट लेने का एक तरीका होने जा रहे हैं, और एक एन-टाइम का प्रदर्शन कर रहे हैं।
यदि आप इस तरह की चीज का उपयोग नहीं कर रहे हैं, तो हर तरह से, अपना खुद का लिखें।

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( )/ आदि सहायक विधि उन लोगों के लिए जीवन को आसान बनाने जा रही है जो सूचियों का उपयोग करना चाहते हैं, जैसे वे सरणियों का उपयोग करते हैं (जैसा कि उन्हें चाहिए), लेकिन उन लोगों के लिए जो अपेक्षाकृत के ईएस 6+ ब्राउज़रों में परिचालन की विलासिता रखते हैं भविष्य में, या बबेल में "ट्रांसप्लिंग" के रूप में, आज आपके पास भाषा में निर्मित विशेषताएं हैं, जो इस प्रकार की चीज़ को अनावश्यक बनाती हैं।

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 ];

सुपर-क्लीन, और बहुत उपयोगी है।
को फिर से देखें रेस्ट और फैले ऑपरेटरों; BabelJS साइट पर उन्हें आज़माएं; यदि आपका तकनीकी स्टैक क्रम में है, तो उन्हें बेबल के साथ उत्पादन और एक निर्माण चरण में उपयोग करें।


गैर-सरणी से सरणी में परिवर्तन का उपयोग करने में सक्षम नहीं होने का कोई अच्छा कारण नहीं है ... ... बस अपने कोड की गड़बड़ी कुछ भी नहीं कर रही है, लेकिन उसी बदसूरत रेखा को चिपकाने, हर जगह।


अब मैं थोड़ा उलझन में हूँ क्योंकि मैंने किया था: कंसोल.लॉग (यह); मुझे हमेशा खिड़की की वस्तु मिल जाएगी और मैंने सोचा कि .call ने इसका मूल्य बदल दिया है
मुहम्मद सालेह

.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
नोरगार्ड

2
@MuhammadSaleh .callका मूल्य परिवर्तन नहीं करता है thisकि आप क्या करने के लिए पारित के अंदर forEach, .callके मूल्य में परिवर्तन this foreach के अंदर । यदि आप इस उलझन में हैं कि जिस फ़ंक्शन को आप चाहते थे, thisउसमें से पास क्यों नहीं forEachकिया गया है, तो आपको thisजावास्क्रिप्ट के व्यवहार को देखना चाहिए । एक परिशिष्ट, लागू केवल यहाँ (और नक्शा / फिल्टर / कम) के रूप में, वहाँ के लिए एक दूसरा तर्क है forEach, जो सेट thisसमारोह के अंदर arr.forEach(fn, thisInFn)या [].forEach.call(arrLike, fn, thisInFn);या सिर्फ उपयोग .bind; arr.forEach(fn.bind(thisInFn));
नोरगार्ड

1
@thetrystero बिल्कुल यही बात है। []सिर्फ वहाँ तो आप कर सकते हैं कि एक सरणी के रूप में है, विधि, और बदलने के सेट आप पर काम करना चाहते हैं। इस तरह दिखता है: सिवाय इसके कि आंतरिक ब्लैक-मैजिक को अंदर सेट करने के लिए समान है जो आपने पास किया था । .call.forEachthis.callfunction (thisArg, a, b, c) { var method = this; method(a, b, c); }thismethodthisArg
नॉरगार्ड

1
तो संक्षेप में ... Array.from ()?
zakius

53

querySelectorAllविधि एक रिटर्न NodeListजो एक सरणी के समान है, है, लेकिन यह काफी एक सरणी नहीं है। इसलिए, यह एक forEachविधि नहीं है (जो सरणी ऑब्जेक्ट्स के माध्यम से इनहेरिट करता है Array.prototype)।

चूँकि a NodeListएक सरणी के समान है, सरणी विधियाँ वास्तव में इस पर काम करेंगी, इसलिए [].forEach.callआप Array.prototype.forEachके संदर्भ में विधि का उपयोग कर रहे हैं NodeList, जैसे कि आप बस करने में सक्षम थे yourNodeList.forEach(/*...*/)

ध्यान दें कि खाली सरणी शाब्दिक विस्तारित संस्करण का सिर्फ एक शॉर्टकट है, जिसे आप संभवतः बहुत बार देखेंगे:

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

7
तो, स्पष्ट करने के लिए, मानक सरणियों के साथ रोजमर्रा के अनुप्रयोगों में उपयोग [].forEach.call(['a','b'], cb)करने ['a','b'].forEach(cb)में कोई फायदा नहीं है , बस जब सरणी जैसी संरचनाओं पर पुनरावृति करने की कोशिश की जा रही है जो forEachअपने स्वयं के प्रोटोटाइप पर नहीं है ? क्या वो सही है?
मैट फ्लेचर

2
@MattFletcher: हाँ यह सही है। दोनों काम करेंगे लेकिन चीजों को ओवरक्लप क्यों करें? बस सरणी पर सीधे विधि को कॉल करें।
जेम्स एलार्डिस

धन्यवाद। और मुझे नहीं पता कि, शायद सिर्फ सड़क के लिए, ALDI और इस तरह से बूढ़ी महिलाओं को प्रभावित करने के लिए। मैं [].forEach():) से चिपक जाऊंगा
मैट फ्लेचर

var section = document.querySelectorAll(".section"); section.forEach((item)=>{ console.log(item.offsetTop) }) अच्छी तरह से काम करता है
daslicht

IE के पुराने संस्करणों में @daslicht नहीं! (है कि ऐसा है, तथापि, का समर्थन forEachसरणियों पर NodeList वस्तुओं, लेकिन नहीं)
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प्रकार से विरासत में नहीं मिलता है । इसलिए हम संदर्भ के साथ ऑब्जेक्ट forEachसे विधि Array.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()सीधे उपयोग कर सकते हैं ।

नोडलिस्ट ऑब्जेक्ट्स नोड्स का संग्रह हैं, जो आमतौर पर Node.childNodes और document.querySelectorAll () जैसी विधियों द्वारा लौटाए जाते हैं।

हालाँकि NodeList एक Array नहीं है, लेकिन forEach () के साथ इस पर पुनरावृति करना संभव है । इसे Array.from () का उपयोग करके एक वास्तविक ऐरे में भी परिवर्तित किया जा सकता है।

हालाँकि, कुछ पुराने ब्राउज़रों ने NodeList.forEach () और न ही Array.from () को लागू नहीं किया है। यह Array.prototype.forEach () का उपयोग करके इसे दरकिनार किया जा सकता है - इस दस्तावेज़ का उदाहरण देखें।


1

एक खाली सरणी forEachमें इसके प्रोटोटाइप में एक संपत्ति होती है जो एक फ़ंक्शन ऑब्जेक्ट है। (खाली सरणी, 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 एक वैश्विक वर्ग है। यदि आप सार्वजनिक पुस्तकालय लिखते हैं तो इस पुनर्संयोजन का उपयोग न करें। जब आप वेबसाइट या नोड.जेएस ऐप पर काम करते हैं तो आत्म-प्रभावकारिता बढ़ाने के लिए यह बहुत सुविधाजनक तरीका है।


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, फिर जैसा कि वर्णन callकिया गया है , का उपयोग सरणी के नोडलिस्ट (इस) पर फ़ंक्शन को निष्पादित करने के लिए किया जाता है।

किसी दिए गए इस मान और व्यक्तिगत रूप से प्रदान किए गए तर्कों के साथ एक फ़ंक्शन को कॉल करता है। MDN


जबकि []वास्तव में हमेशा एक सरणी वापस आ जाएगी, जबकि window.Arrayकुछ के साथ ओवरराइट किया जा सकता है (सभी पुराने ब्राउज़रों में), इसलिए भी किसी भी window.Array.prototype.forEachचीज़ से ओवरराइट किया जा सकता है। किसी भी मामले में सुरक्षा की कोई गारंटी नहीं है, ब्राउज़रों में जो गेटर्स / सेटर या ऑब्जेक्ट सीलिंग / फ्रीजिंग (अपने स्वयं के एक्स्टेंसिबिलिटी मुद्दों के कारण ठंड) का समर्थन नहीं करते हैं।
नोरगार्ड

ओवरराइटिंग की संभावना prototypeया इसके तरीकों के बारे में अतिरिक्त जानकारी जोड़ने के लिए उत्तर को संपादित करने के लिए स्वतंत्र महसूस करें forEach:।
Xotic750

0

नॉरगार्ड ने समझाया कि क्या [].forEach.call() करता है और जेम्स एलार्डिस हमें ऐसा क्यों करते हैं: क्योंकि querySelectorAll एक रिटर्न देता है NodeListजिसमें एक forEach विधि नहीं है ...

जब तक आपके पास क्रोम 51+, फ़ायरफ़ॉक्स 50+, ओपेरा 38, सफारी 10 जैसे आधुनिक ब्राउज़र न हों।

यदि आप एक पॉलीफ़िल नहीं जोड़ सकते हैं :

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

इस पृष्ठ पर बहुत सारी अच्छी जानकारी (देखें उत्तर + उत्तर + टिप्पणी देखें ), लेकिन मेरे पास हाल ही में ओपी के समान प्रश्न था, और पूरी तस्वीर प्राप्त करने के लिए कुछ खुदाई हुई। तो, यहाँ एक छोटा संस्करण है:

लक्ष्य Arrayएक सरणी की तरह तरीकों का उपयोग करना है NodeListजो उन तरीकों को खुद नहीं करता है।

एक पुराने पैटर्न ने ऐरे के तरीकों को सह-चुना Function.call(), और []इसके बजाय एक सरणी शाब्दिक ( ) का उपयोग किया Array.prototypeक्योंकि यह टाइप करने के लिए छोटा था:

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

एक नया पैटर्न (ECMAScript 2015) का उपयोग करना है Array.from():

Array.from(document.querySelectorAll('a')).forEach(a => {})
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.