Cum funcționează array.prototype.map ()

JavaScript este acum un limbaj omniprezent. Odată limitat la utilizarea clientului, acum îl puteți găsi pe servere în mai multe variante. Pe măsură ce JavaScript a crescut, a crescut și arsenalul său de funcții pe care utilizatorii le pot folosi. De cele mai multe ori sunteți mulțumit folosind aceste metode și rareori veți dori să faceți acel pas suplimentar pentru a înțelege ce se întâmplă cu adevărat sub capotă.

Pe această notă, să ia acest pas în plus astăzi și de a explora o funcție foarte popular: Array.prototype.map().

Declinare de responsabilitate : nu voi explica cum să o folosesc map()- exemplul de mai jos ilustrează acest lucru sau puteți găsi numeroase exemple când faceți google. În schimb, să analizăm modul în care harta este implementată de fapt în culise.

map()Metoda creează o nouă matrice cu rezultatul apelarea unei funcții furnizate pe fiecare element din matrice de asteptare.

Exemplu:

var array1 = [1, 4, 9, 16]; // pass a function to map const map1 = array1.map(x => x * 2); console.log(map1); // expected output: Array [2, 8, 18, 32]

Implementare

Să alegem implementarea chiar din gura calului și să încercăm să o disecăm. Mai jos este poliampla MDN. Petreceți ceva timp înțelegând codul și copiați-l și rulați-l pe computer. Dacă sunteți un dezvoltator JavaScript începător / intermediar, cu siguranță veți întâlni cel puțin câteva întrebări.

/*Array.prototype.map implementation*/ Array.prototype.map = function (callback/*, thisArg*/) { var T, A, k; if (this == null) { throw new TypeError('this is null or not defined'); } var O = Object(this); var len = O.length >>> 0; if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { T = arguments[1]; } A = new Array(len); k = 0; while (k < len) { var kValue, mappedValue; if (k in O) { kValue = O[k]; mappedValue = callback.call(T, kValue, k, O); A[k] = mappedValue; } k++; } return A; };

Am evidențiat câteva întrebări frecvente care ar putea apărea în comentariile de cod de mai jos.

/*Array.prototype.map implementation*/ Array.prototype.map = function (callback/*, thisArg*/) { var T, A, k; if (this == null) { throw new TypeError('this is null or not defined'); } var O = Object(this); var len = O.length >>> 0;// QUESTION 1 : What is the need for this line of code? if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { T = arguments[1]; } // QUESTION 2 :What is the need for the if condition and why are we assiging T=arguments[1]? A = new Array(len); k = 0; while (k < len) { var kValue, mappedValue; if (k in O) { kValue = O[k]; mappedValue = callback.call(T, kValue, k, O); // QUESTION 3: why do we pass T,k and O when all you need is kvalue? A[k] = mappedValue; } k++; } return A; };

Să ne adresăm fiecăruia dintre ei începând de jos

ÎNTREBARE 3: De ce trecem T, k și O când tot ce aveți nevoie este kValue?

mappedValue = callback.call(T, kValue, k, O);

Aceasta este cea mai simplă dintre cele trei întrebări, așa că am ales asta pentru a începe. În majoritatea cazurilor, transmiterea valorii k către apelare va fi suficientă, dar:

  • Ce se întâmplă dacă aveți un caz de utilizare în care trebuie să efectuați o operație numai pe orice alt element? Ei bine, aveți nevoie de un index care este (k) .
  • În mod similar, ar putea exista și alte cazuri de utilizare în care este nevoie ca matricea (O) să fie disponibilă în callback.
  • De ce T ? Deocamdată să știți că T este transmis pentru a menține contextul. Veți înțelege mai bine acest lucru odată ce ați terminat cu întrebarea 2.

ÎNTREBAREA 2: Care este necesitatea condiției if și de ce atribuim T = argumente [1]?

if (arguments.length > 1) { T = arguments[1]; }

Funcția de hartă din implementarea de mai sus are două argumente: callback și opțional thisArg . Callback este un argument obligatoriu, în timp ce thisArg este opțional.

Se poate trece ceea ce ar trebui să fie valoarea „aceasta” din callback, oferind al doilea argument opțional. Acesta este motivul pentru care codul verifică dacă există mai multe argumente și atribuie al doilea argument opțional unei variabile care poate fi transmisă apelului invers.

Pentru a ilustra mai bine, să presupunem că aveți o cerință falsă în care trebuie să returnați numărul / 2 dacă este divizibil cu 2, iar dacă nu este divizibil cu 2, trebuie să returnați numele de utilizator al persoanei care sună. Codul de mai jos ilustrează modul în care puteți face acest lucru:

const myObj = { user: "John Smith" } var x = [10, 7]; let output = x.map(function (n) { if (n % 2 == 0) { return n / 2; } else { return this.user } }, myObj) // myObj is the second optional argument arguments[1] console.log(output); // [5,'John Smith'] //if you run the program without supplying myObj it would be //undefined as it cannot access myObj values console.log(output); // [ 5, undefined ]

ÎNTREBARE 1: Care este nevoia acestei linii de cod?

var len = O.length >>> 0

Acesta a luat ceva timp pentru a-mi da seama. Se întâmplă multe în această linie de cod. În JavaScript, aveți capacitatea de a redefini „acest” în cadrul unei funcții prin invocarea metodei folosind apel . Puteți face acest lucru folosind și bind sau aplicați , dar pentru această discuție permiteți să rămâneți cu apelul.

const anotherObject={length:{}} const myObj = { user: "John Smith" } var x = [10, 7]; let output = x.map.call(anotherObject,function (n) { if (n % 2 == 0) {return n / 2;} else {return this.user} }, myObj)

Când invocați apelul,primul parametru ar fi contextul în care se execută funcția de hartă. Prin trimiterea parametrului, suprascrieți „acesta” în interiorul hărții cu „acesta” al altui obiect.

Dacă observați, proprietatea length a anotherObject este un obiect gol și nu un număr întreg. Dacă pur și simplu folosiți O.length în loc de O.length> >> 0 ar rezulta o valoare nedefinită. Prin deplasarea zero, efectiv convertiți orice fracție și non întreg într-un număr întreg. În acest caz, rezultatul va fi forțat la 0.

Majoritatea cazurilor de utilizare nu vor avea nevoie de această verificare, dar ar putea exista un caz de margine în care acest tip de scenariu trebuie tratat. Programatorii buni care au proiectat specificația chiar l-au gândit bine! Vorbind despre specificație, puteți găsi de fapt specificațiile despre modul în care fiecare funcție trebuie implementată în Ecmascript aici:

Specificația limbajului ECMAScript - ECMA-262 Edition 5.1

Acest document și posibilele sale traduceri pot fi copiate și furnizate altora, precum și lucrări derivate care comentează ...

www.ecma-international.org

Specificația ( pasul 3 ) spune clar că lungimea trebuie să fie un număr întreg nesemnat pe 32 de biți. Acesta este motivul pentru care schimbăm umplerea zero pentru a ne asigura că lungimea este un număr întreg, deoarece harta în sine nu necesită ca această valoare să fie un obiect Array.

Asta e!

Aș dori să mulțumesc cuplului de oameni, nu i-am întâlnit niciodată, dar au avut amabilitatea de a-și lua timp (în forumurile de pe internet) și de a mă ajuta să înțeleg puține nuanțe.

Salathiel Genese, Jordan Harband - mulțumesc!

Notă: dacă sunteți blocat pe o altă linie de cod, nu ezitați să introduceți acest lucru în comentarii și voi face tot posibilul să vă clarific.

Vă mulțumim pentru timp și codificare fericită!