Pastarieji keleri metai parodė, kad žiniatinklio svetainių kūrimas keičiasi. Kadangi naršyklėse atsirado daugybė funkcijų ir API, turėjome jas tinkamai naudoti. Kalba, kuriai esame dėkingi už šią garbę, buvo JavaScript.
Iš pradžių kūrėjai nebuvo įsitikinę, kaip jis sukurtas, ir naudodami šį scenarijų patyrė daugiausia neigiamų įspūdžių. Ilgainiui paaiškėjo, kad ši kalba turi didelį potencialą, o vėlesni "ECMAScript" standartai kai kurias mechanikas padaro žmogiškesnes ir, paprasčiausiai, geresnes. Šiame straipsnyje apžvelgsime kai kuriuos iš jų.
Vertybių tipai JS
Gerai žinoma tiesa apie JavaScript yra tai, kad viskas čia yra objektas. Tikrai viskas: masyvai, funkcijos, eilutės, skaičiai ir net loginės eilutės. Visų tipų reikšmės yra atvaizduojamos objektais ir turi savo metodus bei laukus. Tačiau juos galime suskirstyti į dvi kategorijas: primityvus ir struktūriškus. Pirmosios kategorijos reikšmės yra nekintamos, t. y. galime iš naujo priskirti tam tikram kintamajam naują reikšmę, bet negalime keisti pačios esamos reikšmės. Antrosios - tai reikšmės, kurias galima keisti, todėl jas reikėtų interpretuoti kaip savybių rinkinius, kuriuos galime pakeisti arba tiesiog iškviesti tam skirtus metodus.
Deklaruotų kintamųjų taikymo sritis
Prieš pradėdami gilintis, paaiškinkime, ką reiškia taikymo sritis. Galima sakyti, kad sritis yra vienintelė sritis, kurioje galime naudoti deklaruotus kintamuosius. Prieš ES6 standartą galėjome deklaruoti kintamuosius naudodami var sakinį ir suteikti jiems globalią arba lokalią sritį. Pirmoji sritis leidžia mus pasiekti tam tikrus kintamuosius bet kurioje programos vietoje, o antrasis skirtas tik konkrečiai sričiai - dažniausiai funkcijai.
Nuo standarto ES2015, JavaScript yra trys kintamųjų deklaravimo būdai, kurie skiriasi raktiniu žodžiu. Pirmasis aprašytas anksčiau: var raktiniu žodžiu deklaruoti kintamieji priskiriami dabartinės funkcijos kūnui. ES6 standartas leido deklaruoti kintamuosius žmoniškesniais būdais - priešingai nei var sakiniai, const ir let sakiniais deklaruojami kintamieji apima tik bloką. Tačiau, JS const teiginį traktuoja gana neįprastai, palyginti su kitais programavimo kalbos - vietoj išliekamosios vertės išsaugoma išliekamoji nuoroda į vertę. Trumpai tariant, galime keisti objekto, deklaruojamo su const teiginiu, savybes, bet negalime perrašyti šio kintamojo nuorodos. Kai kas sako, kad ES6 var alternatyva iš tikrųjų yra let sakinys. Ne, taip nėra, o var teiginys nėra ir tikriausiai niekada nebus atšauktas. Gera praktika - vengti var teiginių, nes dažniausiai jie mums sukelia daugiau problemų. Savo ruožtu turime piktnaudžiauti const teiginiais, kol nereikia keisti jo nuorodos - tada turėtume naudoti let.
Netikėtos srities elgsenos pavyzdys
Pradėkime nuo šių dalykų kodas:
(() => {
for (var i = 0; i {
console.log(`"i" reikšmė: ${i}`);
}, 1000);
}
})();
Pažvelgus atrodo, kad ciklas for iteruoja i reikšmę ir po sekundės registruoja iteratoriaus reikšmes: 1, 2, 3, 4, 5. Tačiau taip nėra. Kaip minėjome, var sakinys skirtas kintamojo vertei išlaikyti visą funkcijos kūną; tai reiškia, kad antroje, trečioje ir t. t. iteracijoje kintamojo i vertė bus pakeista kita verte. Galiausiai ciklas baigiasi, o laiko žymos rodo mums tokį dalyką: 5, 5, 5, 5, 5, 5, 5. Geriausias būdas išlaikyti dabartinę iteratoriaus reikšmę - vietoj jos naudoti teiginį let:
(() => {
for (let i = 0; i {
console.log(`"i" reikšmė: ${i}`);
}, 1000);
}
})();
Pirmiau pateiktame pavyzdyje i reikšmės sritį išlaikome dabartiniame iteracijos bloke, tai vienintelė sritis, kurioje galime naudoti šį kintamąjį, ir niekas negali jo pakeisti už šios srities ribų. Šiuo atveju rezultatas yra toks, kokio ir tikėtasi: 1 2 2 3 4 5. Pažvelkime, kaip šią situaciją spręsti naudojant var sakinį:
(() => {
for (var i = 0; i {
setTimeout(() => {
console.log(`Vertė "j": ${j}`);
}, 1000);
})(i);
}
})();
Kadangi var teiginys skirtas vertei išlaikyti funkcijos bloko viduje, turime iškviesti apibrėžtą funkciją, kuri priima argumentą - iteratoriaus dabartinės būsenos vertę - ir tada tiesiog kažką daryti. Niekas už deklaruotos funkcijos ribų nepakeis j reikšmės.
Klaidingų lūkesčių dėl objektų verčių pavyzdžiai
Dažniausiai pastebėtas nusikaltimas yra susijęs su struktūrinių elementų galios ignoravimu ir jų savybių, kurios keičiamos ir kituose kodo elementuose, keitimu. Greitai pažvelkite:
const DEFAULT_VALUE = {
favoriteBand: 'The Weeknd'
};
const currentValue = DEFAULT_VALUE;
const bandInput = document.querySelector('#favorite-band');
const restoreDefaultButton = document.querySelector('#restore-button');
bandInput.addEventListener('input', () => {
currentValue.favoriteBand = bandInput.value;
}, false);
restoreDefaultButton.addEventListener('click', () => {
currentValue = DEFAULT_VALUE;
}, false);
Nuo pradžių: tarkime, kad turime modelį su numatytosiomis savybėmis, saugomą kaip objektas. Norime turėti mygtuką, kuris atkurtų numatytąsias įvesties reikšmes. Užpildę įvestį tam tikromis reikšmėmis, atnaujiname modelį. Po akimirkos manome, kad numatytasis pasirinkimas buvo tiesiog geresnis, todėl norime jį atkurti. Paspaudžiame mygtuką... ir nieko neįvyksta. Kodėl? Todėl, kad ignoruojame nurodomų reikšmių galią.
Ši dalis: const currentValue = DEFAULTVALUE nurodo JS: paimkite nuorodą į DEFAULTVALUE reikšmę ir priskirkite ją kintamajam currentValue. Tikroji vertė atmintyje saugoma tik vieną kartą, o abu kintamieji rodo į ją. Keisti kai kurias savybes vienoje vietoje reiškia keisti jas kitoje vietoje. Turime keletą būdų, kaip išvengti tokių situacijų. Vienas iš jų, tenkinantis mūsų poreikius, yra skleidimo operatorius. Sutvarkykime savo kodą:
const DEFAULT_VALUE = {
favoriteBand: 'The Weeknd'
};
const currentValue = { ...DEFAULT_VALUE };
const bandInput = document.querySelector('#favorite-band');
const restoreDefaultButton = document.querySelector('#restore-button');
bandInput.addEventListener('input', () => {
currentValue.favoriteBand = bandInput.value;
}, false);
restoreDefaultButton.addEventListener('click', () => {
currentValue = { ...DEFAULT_VALUE };
}, false);
Šiuo atveju skleidimo operatorius veikia taip: iš objekto paimamos visos savybės ir sukuriamas naujas objektas, užpildytas šiomis savybėmis. Dėl to reikšmės currentValue ir DEFAULT_VALUE nebenurodo į tą pačią vietą atmintyje ir visi pokyčiai, taikomi vienai iš jų, neturės įtakos kitoms.
Gerai, tad klausimas: ar viskas susiję su stebuklinguoju sklaidos operatoriumi? Šiuo atveju - taip, tačiau mūsų modeliams gali prireikti daugiau sudėtingumo nei šiame pavyzdyje. Jei naudojame įterptus objektus, masyvus ar bet kokias kitas struktūras, aukščiausio lygio nurodomos vertės skleidimo operatorius turės įtakos tik aukščiausiam lygiui, o nurodomos savybės vis tiek dalysis ta pačia vieta atmintyje. Šiai problemai spręsti yra daug sprendimų, viskas priklauso nuo jūsų poreikių. Galime klonuoti objektus kiekviename gylio lygyje arba, atlikdami sudėtingesnes operacijas, naudoti tokias priemones kaip immer, kurios leidžia beveik neskausmingai rašyti nekeičiamą kodą.
Viską sumaišykite
Ar galima naudoti žinių apie apimtis ir verčių tipus derinį? Žinoma, galima! Sukurkime ką nors, kas naudoja abu šiuos dalykus:
const useValue = (defaultValue) => {
const value = [...defaultValue];
const setValue = (newValue) => {
value.length = 0; // sudėtingas būdas išvalyti masyvą
newValue.forEach((item, index) => {
value[index] = item;
});
// atlikite kai kuriuos kitus veiksmus
};
grąžinti [value, setValue];
};
const [animals, setAnimals] = useValue(['cat', 'dog']);
console.log(animals); // ['cat', 'dog']
setAnimals(['arklys', 'karvė']);
console.log(animals); // ['arklys', 'karvė']);
Paaiškinkime, kaip šis kodas veikia eilutė po eilutės. Funkcija useValue sukuria masyvą pagal argumentą defaultValue; ji sukuria kintamąjį ir kitą funkciją - jo modifikatorių. Šis modifikatorius priima naują reikšmę, kuri sudėtingu būdu pritaikoma esamai reikšmei. Funkcijos pabaigoje grąžiname reikšmę ir jos modifikatorių kaip masyvo reikšmes. Toliau naudojame sukurtą funkciją - deklaruojame animals ir setAnimals kaip grąžinamas reikšmes. Naudodami jų modifikatorių patikrinkite, ar funkcija veikia kintamąjį animal - taip, tai veikia!
Bet palaukite, kas būtent šiame kode yra įmantraus? Nuoroda išsaugo visas naujas reikšmes, o į šį modifikatorių galite įterpti savo logiką, pvz. kai kurios API arba ekosistemos dalis kuris be jokių pastangų užtikrina jūsų duomenų srautą. Šis sudėtingas modelis dažnai naudojamas modernesnėse JS bibliotekose, kur funkcinė programavimo paradigma leidžia išlaikyti ne tokį sudėtingą kodą, kurį lengviau skaityti kitiems programuotojams.
Santrauka
Supratimas, kaip veikia kalbos mechanika, leidžia mums rašyti sąmoningesnį ir lengvesnį kodą. Nors JS nėra žemo lygio kalba ir verčia mus turėti tam tikrų žinių apie tai, kaip priskiriama ir saugoma atmintis, vis tiek turime saugotis netikėto elgesio modifikuojant objektus. Kita vertus, piktnaudžiavimas verčių klonais ne visada yra teisingas būdas, o neteisingas naudojimas turi daugiau trūkumų nei privalumų. Teisingas duomenų srauto planavimo būdas - apsvarstyti, ko reikia ir su kokiomis galimomis kliūtimis galite susidurti įgyvendindami programos logiką.
Skaityti daugiau: