Fereastra span atribut - Înlănțuirea apelurilor .bind() în JavaScript. Rezultat neașteptat? Schimbarea mai multor elemente și apelarea unui lanț de metode

Unul dintre lucrurile care face jQuery atât de clar și expresiv este că apelarea unei metode pe un obiect jQuery va schimba de obicei toate elementele pe care le conține. acest obiect. Spun „de obicei” pentru că unele metode efectuează operații care nu au sens pe mai multe elemente; veți vedea acest lucru în capitolele următoare. Arată cât de ușor face viața jQuery în comparație cu API-ul DOM de bază.

Lista 5-21: Lucrul cu elemente multiple ... ; var labelElems = document.getElementsByTagName("label"); pentru (var i = 0; i< labelElems.length; i++) { labelElems[i].style.color = "blue"; } }); ...

În acest exemplu, selectez toate elementele de etichetă din document și schimb valoarea proprietății culoare la albastru. În jQuery fac asta într-o singură expresie, dar cu API-ul DOM este nevoie de mai mult efort. De asemenea, cred că semnificația expresiei jQuery este evidentă, dar aceasta este părerea mea personală.

O altă caracteristică foarte frumoasă a obiectului jQuery este că implementează interfață fluidă. Aceasta înseamnă că oriunde apelați o metodă care modifică conținutul unui obiect, rezultatul acelei metode va fi un obiect jQuery diferit. Acest lucru poate părea simplu, dar permite un lanț de metode așa cum se arată în .

Lista 5-22: Metode de înlănțuire pe un obiect jQuery ... $(document).ready(function () ( $("etichetă").css("culoare", "albastru").css("dimensiunea fontului" , ".75em"); var labelElems = document.getElementsByTagName ("etichetă"); for (var i = 0; i< labelElems.length; i++) { labelElems[i].style.color = "blue"; labelElems[i].style.fontSize = ".75em"; } }); ...

În acest exemplu, am creat un obiect jQuery folosind funcția $, numit metoda css și setat valoarea pentru proprietatea color, apoi am apelat din nou metoda css, de data aceasta pentru a seta valoarea proprietății font-size. De asemenea, am demonstrat o soluție echivalentă folosind API-ul DOM. Vezi că nu durează mult mai multă muncă pentru a obține același rezultat pentru că există pentru buclă, care listează elementele selectate.

Beneficiul real vine atunci când utilizați un lanț de metode care fac modificări mai semnificative colecției de elemente conținute într-un obiect jQuery. Este prezentat un exemplu.

Lista 5-23: Mai multe exemplu complex folosind un lanț de metode $(document).ready(function () ( $("etichetă").css("culoare", "albastru").add("input") .filter("").css(" dimensiunea fontului", ".75em"); var elems = document.getElementsByTagName ("etichetă"); pentru (var i = 0; i< elems.length; i++) { elems[i].style.color = "blue"; if (elems[i].getAttribute("for") != "snowdrop") { elems[i].style.fontSize = ".75em"; } } elems = document.getElementsByTagName("input"); for (var i = 0; i < elems.length; i++) { if (elems[i].getAttribute("name") != "rose") { elems[i].style.fontSize = ".75em"; } } });

Acest exemplu demonstrează bine flexibilitatea jQuery. Să ne uităm la fiecare metodă individual pentru a înțelege cum funcționează. Să începem cu asta:

$(„etichetă”).css(„culoare”, „albastru”)

Acesta este un început bun și simplu. Am selectat toate elementele de etichetă din document și pentru toate proprietate CSS culoarea este setată la albastru. Urmatorul pas:

$(„etichetă”).css(„culoare”, „albastru”).add(„intrare”)

Metoda add adaugă elemente care se potrivesc cu selectorul specificat la un obiect jQuery. ÎN în acest caz, Am selectat toate elementele de intrare, valoare atributul numelui care trandafir nu este. Aceste elemente sunt combinate cu elementele selectate anterior și am un complex de elemente de etichetă și de intrare. Metoda de adăugare va fi discutată mai detaliat în Capitolul 6. Iată următoarea adăugare:

$(„etichetă”).css(„culoare”, „albastru”).add(„intrare”).filtru(„”)

Metoda de filtrare elimină toate elementele din obiectul jQuery care nu se potrivesc cu condiția specificată. Voi vorbi despre această metodă mai detaliat în Capitolul 6 și în acest moment este suficient să știu că acest lucru îmi permite să elimin orice element dintr-un obiect jQuery a cărui valoare pentru atribut este ghiocel .

$("etichetă").css("culoare", "albastru").add("input") .filter("").css("dimensiunea fontului", ".75em");

Pasul final este să apelați din nou metoda css, de data aceasta pentru a specifica valoarea .75em pentru proprietatea font-size. Și așa rezultat final acest lucru:

  • Toate elementele de etichetă au proprietatea lor de culoare CSS setată la albastru.
  • Toate elementele de etichetă, cu excepția unuia, a cărui valoare a atributului for este ghiocel , au proprietatea CSS pentru dimensiunea fontului setată la .75em.
  • Toate elementele de intrare care au o valoare de atribut nume, alta decât rose, au proprietatea lor CSS font-size setată la .75em.
  • Este mult mai dificil să obții același rezultat folosind API-ul DOM și am întâmpinat unele dificultăți în timpul scrierii acestui script. De exemplu, m-am gândit că aș putea folosi metoda document.querySelectorAll descrisă în Capitolul 2 pentru a selecta elementele de intrare folosind selectorul de intrare, dar se dovedește că acest tip de filtru de atribute nu funcționează cu această metodă. Apoi am încercat să evit dublarea apelului de metodă pentru specificarea valorii pentru dimensiunea fontului combinând rezultatele celor două getElementsByTagName , dar experiența a fost destul de dureroasă. Nu vreau să vă plictisesc cu detaliile, mai ales dacă aveți un interes puternic pentru jQuery și pentru a citi această carte. Dar ceea ce vreau să spun este că jQuery oferă un nivel de flexibilitate și expresivitate care nu poate fi atins folosind API-ul DOM de bază.

    Când jQuery Biblioteca JavaScript, a fost lansat în ianuarie 2006, toată lumea s-a gândit: „o altă jucărie frumoasă”. Folosirea selectoarelor CSS ca bază a fost cu siguranță o idee bună, dar utilizarea transformărilor înlănțuite părea puțin complicată, iar biblioteca în sine nu părea să acopere totul. cazuri posibile. jQuery a fost atunci privit doar ca o soluție temporară și temporară.

    Abia după câteva luni a devenit clar că jQuery era pur și simplu o lucrare de inginerie. Ea acoperă suficient de priceput gamă largă funcții de zi cu zi și, în același timp, oferă un API convenabil pentru extensii cu care puteți adăuga orice altă funcționalitate. Abstracția îi este inerentă la nivelul de bază - despre care vorbim despre alegerea elementelor model de obiect documente (DOM) - și ea profită la maximum de ea. Și cel mai important, folosirea acestei biblioteci implică următoarele stil bunîn programare și se potrivește bine cu alte părți ale codului JavaScript.

    Majoritate recenzii moderne jQuery se concentrează pe designeri și dezvoltatori fără experiență. Voi încerca să explic de ce este necesar și pentru programatorii experimentați.

    Spațiu de nume (spație de nume)

    Cheia pentru crearea unui cod JavaScript bun pentru utilizare ulterioară este o gestionare atentă a spațiului de nume. JavaScript are un singur spațiu de nume global (obiectul fereastră) și mulți programatori (și chiar unele biblioteci) îl împrăștie în mod inutil. Variabilele globale sunt rele! Dezvoltatorii mai prudenți își reduc la minimum pătrunderea în acest spațiu prin utilizarea unor tehnici, cum ar fi un model modular.
    Intră jQuery un singur obiect la spațiul de nume global - funcție/obiect jQuery. Orice altceva este fie o proprietate jQuery directă, fie o metodă a unui obiect returnat de un apel de funcție jQuery.

    Dar îmbunătățirile lingvistice? Majoritatea bibliotecilor oferă o aparență de funcționalitate de afișare, filtrare și tăiere care, din păcate, lipsește din motoarele JavaScript incluse în majoritatea browserelor. Unele biblioteci le extind direct pe cele încorporate în JavaScript Clasele de șiruriși Array, dar nici nu complet sigur. String.prototype și Array.prototype sunt spații de nume globale independente, iar adăugarea oricăror proprietăți la ele implică riscul de coliziuni asociat cu utilizarea acelorași nume de variabile în contexte diferite.

    jQuery are o serie de funcții care se extind Capacitate JavaScript, dar fiecare este accesibil doar ca proprietate a obiectului jQuery: jQuery.each, jQuery.extend, jQuery.grep, jQuery.map, jQuery.merge și jQuery.trim. Ele, prin definiție, nu vor intra în conflict cu niciun alt cod.

    Infama funcție $
    De fapt, jQuery introduce mai mult de un caracter în spațiul de nume global, există și $: este folosit ca prescurtare pentru jQuery. Acest lucru se face destul de ușor: dacă aveți nevoie din nou de vechea funcție $ (de exemplu, dacă aveți o bucată de cod bazată pe Prototype), puteți apela jQuery.noConflict() pentru a vă întoarce vechea functie $.
    Dacă trebuie să restricționați utilizarea funcției $ pentru jQuery fără teama de coliziuni cu orice altă utilizare a funcției globale $, documentația jQuery sugerează următoarea metodă:

    (funcție($) ( // În acest bloc, $ se referă la jQuery // Frumos, nu? ))(jQuery);

    Utilizarea omniprezentă a lui $ în jQuery nu este altceva decât un truc inteligent. Dar dacă o luăm în considerare doar în contextul jQuery, atunci această soluție pare foarte flexibilă.

    Selectarea elementelor
    Fiecare instrucțiune jQuery începe prin a selecta unul sau mai multe noduri DOM. Sintaxa selectorului jQuery (limbajul intern al acestei biblioteci) este un hibrid interesant de CSS 1, 2, puțin CSS 3, puțin XPath și alte câteva extensii. Nu vom intra în detalii, dăm doar câteva exemple utile:

    JQuery("div.panel") Toate div-urile cu class="panel" jQuery("p#intro") Paragraf cu id="intro" jQuery("div#content a:visible") Toate linkurile vizibile din interiorul div-urilor cu id= "conținut" jQuery("input[@name=email]") Toate câmpurile de intrare cu name="email" jQuery("table.orders tr:odd") Toate rândurile pare dintr-un tabel cu class="orders" jQuery(" a [@href^="http://"]") Toate linkurile externe (cele care încep cu http://) jQuery("p[a]") Toate paragrafele care conțin cel puțin un link

    Cele mai interesante din cele de mai sus sunt :visible și :odd, care sunt specifice numai jQuery. De asemenea, merită remarcat faptul că selectarea atributelor folosește semnul @, care este mai consistent cu XPath decât CSS 2.

    Limbajul selectorului este foarte bogat și foarte asemănător cu expresii obisnuite, așa că timpul petrecut studiul va merita din plin.

    Facem ceva cu ei
    Obiectul pe care selectorii jQuery îl returnează este o bestie destul de interesantă. Este o colecție de elemente DOM și se comportă puțin ca o matrice: are o proprietate de lungime, elementele sale pot fi accesate prin index și (mai important) Firebug o tratează ca pe o matrice atunci când o afișează în consola sa. Aceasta nu este altceva decât o iluzie frumoasă: un set de elemente este, de fapt, un obiect jQuery care are număr mare metode de selectare, modificare sau extindere a setului existent.
    jQuery are trei diferite categorii metode: primul manipulează toate elementele care se potrivesc cu modelul, al doilea returnează valoarea de la primul element găsit, iar al treilea schimbă selecția în sine.

    Nu vom enumera totul metodele disponibile(puteți vedea acest lucru și pe visualjquery.com), dar iată câteva exemple. Dacă aveți Firebug, le puteți încerca singur: trebuie doar să utilizați fila Inserare jQuery (javascript:void(function())(var s=document.createElement(‘script’);
    s.src=’http://code.jquery.com/jquery-1.1.2.js’; document.getElementsByTagName('head').appendChild(s);)())) pentru a încărca biblioteca în sine pentru orice pagină, apoi lipiți exemplele de cod în consola Firebug (notă: puteți face acest lucru fără Firebug: încărcați doar jQuery folosind linkurile specificate și apelați exemplele date în bara de adresa browser, fără a uita javascript: la început și un fel de alertă la sfârșit (pentru ca valoarea returnată să nu fie afișată pe pagină)).

    JQuery("div#primary").width(300); Setează lățimea div id="primary" la 300 de pixeli. jQuery("p").css("line-height", "1.8em"); Setează înălțimea liniei la 1,8 em pentru toate paragrafele. jQuery("li:odd").css((culoare: "alb", culoarea fundal: "negru")); Aplica 2 reguli CSS pentru fiecare articol din lista; te rog noteaza asta funcția css() poate lua un obiect foaie de stil în loc de două șiruri de caractere. jQuery ("a[@href^="http://"]").addClass("extern").attr("ţintă", "_blank"); Adaugă clasa „externă” tuturor linkuri externe(cele care încep cu http://), apoi adaugă target="_blank" pentru a crește diferența. ÎN în acest exemplu Se utilizează lanțul de apeluri descris mai jos. jQuery("blockquote").each(function(el) ( alert(jQuery(this).text()) )); Pentru fiecare etichetă blockquote de pe pagină, aceasta afișează o alertă cu conținutul său text (inclusiv etichete HTML). jQuery("a").html("Clic aici!"); Înlocuiește tot textul din linkurile dintr-o pagină cu un mesaj „Click Here!”. Mai jos sunt câteva exemple de metode care returnează valoarea de la primul element găsit în șablon: var width = jQuery("div").width(); Care este lățimea primului div de pe pagină? var src = jQuery("img").attr("src"); Ce înseamnă atributul src la prima poza de pe pagina? var culoare = jQuery("h1").css("culoare"); Ce culoare este primul h1?

    Să remarcăm simetria convenabilă a unor astfel de metode: ele sunt folosite atât pentru a seta atribute (când iau 2 argumente sau obiectul care este transmis are mai multe proprietăți), cât și pentru a citi valorile acestor atribute (dacă este trecut un singur argument) . Această simetrie este folosită peste tot biblioteca jQuery, ceea ce face foarte ușor să vă amintiți API-ul.
    Există, de asemenea, mai multe metode care modifică întregul set de elemente găsite. Multe dintre ele oferă, de asemenea, navigare în arborele DOM:

    JQuery("div").not("[@id]") Returnează toate div-urile care nu au un atribut id. jQuery("h2").parent() Returnează toate elementele care sunt părinții imediati ai lui h2. jQuery("blockquote").children() Returnează toate elementele imbricate în blockquote. jQuery("p").eq(4).next() Găsește al cincilea paragraf de pe pagină, apoi găsește elementul următor(adică vecinul imediat din dreapta). jQuery("input:text:first").parents("form") Găsește element părinte pentru un formular care conține primul câmp de intrare tip="text" din pagină. Parametrul opțional pentru părinți() este un alt selector.

    Lanțuri de apeluri

    Echipa jQuery se mândrește adesea cu suportul bibliotecii pentru apelurile înlănțuite, mergând până acolo încât să facă afirmații precum „jQuery este conceput pentru a schimba modul în care codificați cu JavaScript” chiar pe pagina principala. Această tehnică este considerată mai mult o ramură laterală decât o cale către viitor, dar puteți utiliza jQuery fără a fi nevoie să vă ocupați de lanțuri lungi de apeluri.
    Lanțurile pot fi folosite pentru mai multe trucuri interesante. Pe lângă utilizarea unui set de selecții DOM, puteți folosi metoda end() a jQuery pentru a naviga prin teancul de obiecte și a ieși din contextul curent. Este puțin greu de explicat, dar atunci când utilizați o metodă care modifică contextul (obiectului) curent (cum ar fi children() sau filter()), puteți utiliza end() puțin mai târziu pentru a reveni la selecția anterioară. Jesse Skinner conduce bun exemplu utilizați această caracteristică în tutorialul meu Facilitarea dezvoltării AJAX cu jQuery:

    $("form#login") // ascunde toate etichetele cu clasa opțională din interiorul form.find("label.optional").hide().end() // adaugă un chenar roșu la toate câmpurile de tip parolă din form.find( "input:parola").css("border", "1px solid red").end() // adaugă un handler pentru evenimentul de trimitere al formularului. submit(function())( return confirm("Sunt sunteți sigur că doriți să trimiteți datele?" ); ));

    Toate acestea mare transformare Va dura doar o linie. Găsește formularul, găsește unele elemente în interiorul acestuia, le aplică modificări, revine la formular și îi adaugă un handler de evenimente submit().

    Manipularea DOM

    jQuery are mai multe moduri de a face manipulare DOM extinsă. Prima este oarecum neașteptată: Funcția jQuery poate lua o bucată de cod HTML ca argument, care este convertit într-un element DOM (de fapt, este doar un șir):

    var div = jQuery("Un text");

    Puteți folosi un lanț de apeluri pentru a adăuga atribute la div odată ce acesta a fost creat:

    var div = jQuery(„Un text”).addClass(„inserat”).attr(„id”, „foo”); Acum să-l adăugăm la eticheta body: div.appendTo(document.body) Sau să-l inserăm folosind un selector în codul existent: div.prependTo("div#primary")

    Interceptarea evenimentelor

    Toate bibliotecile JavaScript au nevoie de metode pentru a gestiona evenimente, iar jQuery nu face excepție. Ca și în cazul attr() și css(), metodele pentru evenimente pot servi în două scopuri: apelarea lor cu o funcție ca argument atribuie un handler de evenimente, apelarea lor fără argument emulează apariția acelui eveniment:

    JQuery("p").click(function() ( jQuery(this).css("culoare-fond", "rosu"); )); Am setat un handler de clic de mouse pentru toate paragrafele, pe care acestea devin roșii. jQuery("p:first").click() Emulează un clic pentru primul paragraf din pagină. Funcții similare sunt utilizate pentru alte evenimente din browser: trecerea mouse-ului, tastarea, etc. Trebuie remarcat faptul că atunci când apelați handlerul de evenimente cuvânt cheie aceasta se referă la elementul care a provocat acest eveniment; Utilizarea jQuery(this) este un truc comun pentru a apela metode jQuery pe acest element. Câteva funcții legate de evenimente merită o mențiune specială: jQuery("a").hover(function() ( jQuery(this).css("background-culo", "orange"); ), function() ( jQuery( this).css("culoare-fond", "alb"); )); hover() este o prescurtare pentru două evenimente: onmouseover și onmouseout. jQuery("p").one("click", function() ( alert(jQuery(this).html()); )); one() expune un handler de evenimente care va fi șters după primul apel. În exemplul de mai sus, toate paragrafele ar trebui să alerteze asupra conținutului lor după primul clic pe ele. jQuery acceptă, de asemenea, evenimente native prin metodele bind() și trigger() (similar cu click()). Evenimentele personalizate pot prelua argumente transmise folosind o matrice în apelul trigger(): jQuery(document).bind("stuffHappened", function(event, msg) ( alert("Ce s-a întâmplat: " + msg); )); jQuery(document).trigger(„lucruri întâmplate”, [„Bună ziua!”]);

    Programare discretă

    Cele mai bune aplicații web sunt cele care pot funcționa atât cu scripturile dezactivate, cât și cea mai buna metoda Pentru a atinge acest obiectiv va exista JavaScript discret, în care evenimentele sunt atribuite elementelor numai după ce întreaga pagină HTML a utilizatorului s-a încărcat (pentru mai multe informatii detaliate Vă puteți familiariza cu programarea discretă și cu Hijax).
    jQuery are un suport excelent pentru această abordare. În primul rând, paradigma selectorului pentru selectarea unui nod este fundamentală atât pentru jQuery, cât și pentru programarea discretă în general. În al doilea rând, jQuery oferă soluții la problema window.onload, pe baza cercetării lui Dean Edwards asupra modului în care funcționează evenimentul „DOM încărcat” pentru browsere diferite. Puteți configura o funcție de gestionare atunci când DOM-ul este pregătit pentru aceasta:

    jQuery(document).ready(function() ( alert("DOM gata!"); ));

    Și chiar mai mult, puteți scurta această intrare atribuind funcția direct la jQuery():

    jQuery(funcție() (alertă(„DOM gata!”); ));

    jQuery și AJAX

    U jQuery este cel mai bun API-ul AJAX pe care l-am văzut vreodată în biblioteci mari. Cel mai formă simplă Apelul AJAX arată astfel:
    jQuery('div#intro').load('/some/fragment.html');

    Va face o solicitare GET către /some/fragment.html și va introduce codul HTML pe care îl primește în div#intro.
    Ce se întâmplă dacă aveți nevoie de ceva mai complex, cum ar fi afișarea unui indicator de încărcare AJAX? jQuery oferă un set evenimente proprii(ajaxStart, ajaxComplete, ajaxError și altele) pentru utilizare dacă este necesar. Această bibliotecă are și un API mai avansat. nivel scăzut pentru interacțiuni complexe AJAX:

    jQuery.get("/some/script.php", ("nume": "Simon"), function(data) ( alert("Serverul a raspuns: " + date); )); // GET cerere către /some/script.php?name=Simon jQuery.post("/some/script.php", ("nume": "Simon"), function(data) ( alert("Serverul a răspuns: " + date); )); // Solicitare POST către /some/script.php jQuery.getJSON("/some.json", function(json) ( alert("JSON returnat: " + json.foo + " " + json.bar); )) ; // Returnează și convertește răspunsul din /some.json ca JSON jQuery.getScript("/script.js"); // GET cererea către /script.js și eval()

    Extensii

    Având în vedere acest întreg set de funcții în Livrare Standard, merită remarcat faptul că versiunea comprimată a jQuery ocupă doar 20 KB și chiar mai puțin dacă utilizați arhivarea (.gz). Funcționalități suplimentare dincolo de acest domeniu pot fi furnizate prin extensii, care pot adăuga (și fac) noi metode la un obiect jQuery existent. Ai putea să faci așa ceva dacă ai vrea.

    Promisiunea este relativă optiune noua JavaScript, care vă permite să efectuați acțiuni amânate - ceva care a fost rezolvat anterior prin apeluri inverse. Bună introducere această tehnologie este pe site-ul HTML5Rocks, nu o voi repeta.

    Promise are două metode (pe care ne interesează acum) - .then() și .catch() , cărora le sunt transmise funcții de gestionare pentru stările „împlinit” și, respectiv, „respins”. Aceste metode pot fi înlănțuite: prom.then().then().catch().then().catch().catch().… . Întrebare: în ce ordine sunt chemați acești handleri, ce primesc și de ce depinde totul?

    Mai întâi, să ne uităm la construcția prom.then().then() (aici prom este un fel de Promise). Ce este prom.then() aici? Este evident o Promisiune, deoarece are metodele .then() și .catch(). Cum este sens această Promisiune? Poate părea „natural” să presupunem că este același bal care se transmite de-a lungul lanțului, dar de fapt nu este cazul (și este bine că nu este așa).

    Fiecare handler .then() și .catch() returnează o valoare. Chiar dacă o funcție nu returnează o valoare în mod explicit, rezultatul ei este totuși sens nedefinit. Se aplică următoarele reguli:

    • Dacă funcția returnează o Promisiune, atunci aceasta devine noua Promisiune din lanț;
    • Dacă funcția returnează orice altă valoare, atunci este înfășurată în Promise.resolve(...) și devine o Promisiune în starea finalizată;
    • În cele din urmă, dacă o excepție apare într-o funcție, valoarea acelei excepții este înfășurată în Promise.reject(...) și devine o Promisiune în starea respinsă.

    Cea mai utilă aplicare a acestor reguli este capacitatea de a procesa date în lanț. De exemplu, așa:

    Get("data.json") .then(function(response) ( // răspunsul este text date return JSON.parse(response); )).then(function(response) ( // și aici răspunsul este obiectul primit în handlerul anterior console.log("Da JSON!", raspuns); ));

    Dar să revenim la lanțuri arbitrare. Avem o Promisiune, în funcție de starea sa, apelează .then() sau .catch() , cel mai apropiat de ea în lanț. Managerul apelat returnează o nouă Promisiune, care apelează din nou .then() sau .catch() , oricare dintre ele este cel mai apropiat din lanț. Și așa mai departe.

    Să dăm un exemplu (o să-l scriu în sintaxa ES6, e mai simplu):

    Promise.reject("A") .then(x => ( console.log("THEN1", x); )) .catch(x => ( console.log("CATCH1", x); )) .then (x => ( console.log("THEN2", x); )).catch(x => ( console.log("CATCH2", x); ));

    Ce vom primi în consolă? Asta e ceea ce:

    CATCH1 A THEN2 nedefinit

    De ce este asta? Avem o Promisiune în starea eșuată, ceea ce înseamnă că va apela cel mai apropiat handler .catch() din lanț, care este CATCH1. Acest handler nu returnează nimic (adică returnează nedefinit), ceea ce înseamnă că rezultatul este efectuat Promisiune cu valoare nedefinită. Promisiunea finalizată apelează cel mai apropiat .then() , care este THEN2.

    Alt exemplu:

    Promise.reject("A") .catch(x => ( console.log("CATCH1", x); return Promise.reject("B"); )) .then(x => ( console.log("" THEN1", x); )).catch(x => ( console.log("CATCH2", x); )).then(x => ( console.log("THEN2", x); ));

    Rezultat (descoperiți-vă singur de ce este așa):

    CATCH1 A CATCH2 B THEN2 nedefinit

    Apropo, aici sunt niște greble subacvatice. Să avem un lanț prom.then().catch() și să presupunem că .catch() prinde un eșec în prom. Dacă balul are succes, atunci .then() este apelat, dar dacă apare vreo eroare sau excepție în handler-ul său, atunci va returna o nouă Promisiune într-o stare eșuată, ceea ce va face ca .catch() să fie executat, ceea ce va cel mai probabil nu funcționează corect. .k. se așteaptă la date complet diferite.

    Prin urmare, pentru a vă asigura că atât manevrele .then() cât și .catch() se ocupă numai de Promise dată, nu ar trebui să utilizați un lanț de apeluri, ci să apelați .then() cu două argumente. Astfel: prom.then(onResolve, onReject). Apoi, indiferent de ce se întâmplă în onResolve, onReject nu va fi apelat.

    Răspunsul grozav al lui torazaburo mi-a dat o idee. Ar fi posibil ca o funcție precum legarea, în loc să coace receptorul (acesta) în apelul din interiorul închiderii, să o pună ca proprietate pe obiectul funcție și apoi să o folosească atunci când îl apelați. Acest lucru va permite reîncercării să actualizeze proprietatea înainte ca apelul să fie efectuat, oferind efectiv rezultatele așteptate ale reîncercării.

    De exemplu,

    function original_fn() ( document.writeln(JSON.stringify(this)); ) Function.prototype.rebind = function(obj) ( var fn = this; var bound = function func() ( fn.call(func.receiver, argumente); ); bound.receiver = obj; bound.rebind = function(obj) ( this.receiver = obj; return this; ); return bound; ) var bound_fn = original_fn.rebind((foo: "bar")) ; bound_fn(); var rebound_fn = bound_fn.rebind((fred: "barney")); rebound_fn();

    Sau rezultatul de la node.js arată astfel.

    ( foo: "bar" ) ( fred: "barney")

    Rețineți că primul apel de relegare îl apelează pe cel care a fost adăugat la Function.prototype așa cum este numit în funcția normală original_fn, dar al doilea apel apelează relegarea care a fost adăugată ca proprietate a funcției legate (și orice apeluri ulterioare vor suna si asa). Această relegare pur și simplu actualizează receptorul și returnează același obiect funcție.

    A fost posibil să accesați proprietatea receptorului în funcția legată făcând asta expresia funcției numite .

    2018-12-04T00:00Z

    Este tentant să te gândești la bind ca la un fel de modificări caracteristici pentru a utiliza acest nou. În această interpretare (incorectă), oamenii se gândesc la bind adăugând un fel de steag magic la o funcție care îi spune să folosească altceva data viitoare când este apelată. Dacă acesta este cazul, atunci ar trebui să fie posibilă „suprascrierea” și schimbarea steagului magic. Și apoi întrebați, care este motivul pentru limitarea arbitrară a capacității de a face acest lucru?

    Dar nu așa funcționează de fapt. bind creează și revine nou o funcție care, atunci când este apelată, apelează prima funcție cu o anumită valoare. Comportamentul acestei funcții nou create de a utiliza cea specificată pentru a apela funcția originală este înregistrată atunci când funcția este creată. Nu poate fi schimbat mai mult decat elementele interne ale oricarei alte functii returnate de o functie pot fi schimbate ulterior.

    Ar putea ajuta să privim realul implementare simplă lega:

    // NU legarea reală; doar un exemplu Function.prototype.bind = function(ctxt) ( var fn = this; return function bound_fn() ( return fn.apply(ctxt, arguments); ); ) my_bound_fn = original_fn.bind(obj);

    După cum puteți vedea, nicăieri în bound_fn funcția returnată de la bind nu se referă la this cu care a fost apelată funcția legată. Este ignorat, deci

    My_bound_fn.call(999, arg) // 999 este ignorat

    Obj = ( fn: function () ( console.log(this); ) ); obj.fn = obj.fn.bind(other_obj); obj.fn(); // scoate other_obj; obj este ignorat

    Deci, pot lega funcția returnată de la bind „din nou”, dar acest lucru nu împinge înapoi funcția inițială; se leagă doar de o funcție externă care nu afectează funcție internă, deoarece este deja configurat pentru a apela functie de bază cu contextul (această valoare) trecut la bind . Mă pot conecta din nou și din nou, dar tot ce fac este să creez mai multe funcții externe, care poate fi legat de ceva, dar sfârșește prin a apela funcția cea mai interioară returnată de la prima legătură.

    Prin urmare, este oarecum înșelător să spunem că legătura „nu poate fi depășită”.

    Dacă vreau să „restaurez” o funcție, atunci pot crea pur și simplu o nouă legare la funcția originală. Deci, dacă l-am legat o dată:

    Funcția orig() ( ) my_bound_fn = orig.bind(my_obj);

    si apoi o vreau pe a mea functia originala a fost apelat cu alții, apoi nu mai verific funcția aferentă.