de Pradeep Pothineni

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ă.

În această notă, să facem acest pas suplimentar astăzi și să explorăm o funcție foarte populară: Array.prototype.map().

Cum functioneaza arrayprototypemap

Declinare de responsabilitate: Nu voi explica cum să folosesc map()exemplul de mai jos îl ilustrează 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 apelării unei funcții furnizate pe fiecare element din matricea apelantă.

Exemplu:

ad-banner
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, trecerea kValue la sună din nou ar 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 aveți nevoie de matrice (O) să fie disponibil în callback.
  • De ce T? Deocamdată știi asta T este trecut pentru a menține contextul. Veți înțelege acest lucru mai bine după 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: sună din nou și opțional thisArg. Callback este un argument obligatoriu întrucât thisArg este opțional.

Se poate trece ceea ce ar trebui să fie “acest” valoare în interiorul sună din nou prin furnizarea celui de-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ă unde trebuie să returnați numarul 2 dacă este divizibil cu 2 și 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 fișierul “acest” în cadrul unei funcții prin invocarea metodei folosind apel. Puteți face acest lucru folosind lega sau aplica la fel, dar pentru această discuție, rămânem cu apel.

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 folosind apel, primul parametru ar fi contextul în care se execută funcția de hartă. Prin trimiterea parametrului, suprascrieți fișierul “acest” în interiorul hărții cu “acest” al altui obiect.

Dacă observați, lungime proprietatea anotherObject este un obiect gol și nu un număr întreg. Dacă folosiți doar Lungime O în loc de lungime O>>> 0 ar rezulta într-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 – Ediția ECMA-262 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 acest valoarea să fie un obiect Array.

Asta este!

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ă!