Pēdējo gadu laikā ir redzams, ka tīmekļa izstrāde mainās. Tā kā pārlūkprogrammās tika pievienotas daudzas funkcijas un API, mums tās bija jāizmanto pareizā veidā. Valoda, kurai esam parādā šo godu, bija JavaScript.
Sākotnēji izstrādātāji nebija pārliecināti par to, kā tas ir izstrādāts, un, izmantojot šo skriptu, viņiem bija galvenokārt negatīvi iespaidi. Laika gaitā izrādījās, ka šai valodai ir liels potenciāls, un turpmākie ECMAScript standarti padara dažas mehānikas detaļas cilvēcīgākas un, vienkārši, labākas. Šajā rakstā mēs aplūkosim dažus no tiem.
Vērtību veidi JS
Labi zināmā patiesība par JavaScript ir tas, ka šeit viss ir objekts. Patiesi, viss: masīvi, funkcijas, virknes, skaitļi un pat booleans. Visu veidu vērtības tiek attēlotas ar objektiem, un tiem ir savas metodes un lauki. Tomēr mēs tos varam iedalīt divās kategorijās: primitīvie objekti un strukturālie objekti. Pirmās kategorijas vērtības ir nemainīgas, tas nozīmē, ka mēs varam atkārtoti piešķirt kādam mainīgajam jaunu vērtību, bet nevaram mainīt pašu esošo vērtību. Otrajā kategorijā ir vērtības, kuras var mainīt, tāpēc tās jāinterpretē kā īpašību kolekcijas, kuras mēs varam aizstāt vai vienkārši izsaukt metodes, kas paredzētas, lai to izdarītu.
Deklarēto mainīgo darbības joma
Pirms iedziļināties, paskaidrosim, ko nozīmē darbības joma. Var teikt, ka darbības joma ir vienīgā joma, kurā varam izmantot deklarētos mainīgos. Pirms ES6 standarta mēs varējām deklarēt mainīgos ar teikumu var un piešķirt tiem globālu vai lokālu jomu. Pirmā ir joma, kas ļauj mums lai piekļūtu dažiem mainīgajiem jebkurā lietojumprogrammas vietā, bet otrais ir paredzēts tikai konkrētai jomai - galvenokārt funkcijai.
Kopš standarta ES2015, JavaScript ir trīs veidi, kā deklarēt mainīgos, kas atšķiras ar atslēgas vārdu. Pirmais ir aprakstīts iepriekš: mainīgie, kas deklarēti ar atslēgas vārdu var, ir attiecināti uz pašreizējās funkcijas ķermeni. ES6 standarts ļāva mums deklarēt mainīgos ar cilvēcīgākiem paņēmieniem - pretēji var izteikumiem, mainīgie, kas deklarēti ar const un let izteikumiem, ir skopēti tikai uz bloku. Tomēr, JS izturas pret const paziņojumu diezgan neparasti, ja salīdzina ar citiem programmēšanas valodas - tā vietā, lai saglabātu saglabājamo vērtību, tiek saglabāta saglabājamā atsauce uz vērtību. Īsāk sakot, mēs varam mainīt ar const deklarēta objekta īpašības, bet nevaram pārrakstīt šī mainīgā atsauci. Daži apgalvo, ka ES6 alternatīva var patiesībā ir izteikums let. Nē, tas tā nav, un apgalvojums var nav un, visticamāk, nekad netiks atsaukts. Laba prakse ir izvairīties no var izteikumu lietošanas, jo lielākoties tie sagādā mums vairāk problēmu. Savukārt mums ir jāizmanto const izteikumi, līdz brīdim, kad mums ir jāmaina tā atsauce - tad mums ir jāizmanto let.
Negaidītas darbības jomas uzvedības piemērs
Sāksim ar šādu informāciju. kods:
(() => {
for (var i = 0; i {
console.log(`"i" vērtība: ${i}`);
}, 1000);
}
})();
Kad mēs to apskatām, šķiet, ka for cilpa iterē i vērtību un pēc vienas sekundes reģistrē iteratora vērtības: 1, 2, 3, 4, 5. Bet tā tas nav. Kā jau iepriekš minējām, ar apgalvojumu var tiek saglabāta mainīgā vērtība visu funkcijas ķermeni; tas nozīmē, ka otrajā, trešajā un tā tālāk iterācijā mainīgā i vērtība tiks aizstāta ar nākamo vērtību. Visbeidzot, cikls beidzas, un timeout tiksi rāda mums šādu lietu: 5, 5, 5, 5, 5, 5, 5. Labākais veids, kā saglabāt iteratora pašreizējo vērtību, ir tā vietā izmantot let izteikumu:
(() => {
for (let i = 0; i {
console.log(`"i" vērtība: ${i}`);
}, 1000);
}
})();
Iepriekš minētajā piemērā mēs saglabājam i vērtības darbības jomu pašreizējā iterācijas blokā, tā ir vienīgā joma, kurā mēs varam izmantot šo mainīgo, un nekas to nevar pārrakstīt ārpus šīs jomas. Rezultāts šajā gadījumā ir tāds, kā gaidīts: 1 2 2 3 4 5. Aplūkosim, kā rīkoties šajā situācijā ar var izteikumu:
(() => {
for (var i = 0; i {
setTimeout(() => {
console.log(`Value of "j": ${j}`);
}, 1000);
})(i);
}
})();
Tā kā ar apgalvojumu var tiek saglabāta vērtība funkciju bloka iekšienē, mums ir jāizsauc definēta funkcija, kas pieņem argumentu - iteratora pašreizējā stāvokļa vērtību - un pēc tam vienkārši kaut ko jādara. Nekas ārpus deklarētās funkcijas neaizstās j vērtību.
Objektu vērtību nepareizas gaidas piemēri
Visbiežāk izdarītais noziegums, ko es pamanīju, ir saistīts ar strukturālo elementu spēka ignorēšanu un to īpašību maiņu, kas tiek modificētas arī citos koda elementos. Veiciet īsu ieskatu:
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);
No sākuma: pieņemsim, ka mums ir modelis ar noklusējuma īpašībām, kas saglabāts kā objekts. Mēs vēlamies izveidot pogu, kas atjaunotu tās ievades vērtības līdz noklusējuma vērtībām. Pēc tam, kad ievadi ir aizpildīti ar dažām vērtībām, mēs atjauninām modeli. Pēc brīža mēs domājam, ka noklusējuma izvēle bija vienkārši labāka, tāpēc vēlamies to atjaunot. Noklikšķinām uz pogas... un nekas nenotiek. Kāpēc? Tāpēc, ka ignorējam atsaukto vērtību spēku.
Šī daļa: const currentValue = DEFAULTVALUE norāda JS sekojošo: ņem atsauci uz DEFAULTVALUE vērtību un piešķir currentValue mainīgajam ar to. Reālā vērtība atmiņā tiek saglabāta tikai vienu reizi, un abi mainīgie norāda uz to. Dažu īpašību modificēšana vienā vietā nozīmē to modificēšanu citā vietā. Mums ir vairāki veidi, kā izvairīties no šādām situācijām. Viens no tiem, kas atbilst mūsu vajadzībām, ir izplatīšanas operators. Labosim mūsu kodu:
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);
Šajā gadījumā izplatīšanas operators darbojas šādi: tas paņem visas objekta īpašības un izveido jaunu objektu, kurā tās ir norādītas. Pateicoties tam, currentValue un DEFAULT_VALUE vērtības vairs nenorāda uz vienu un to pašu vietu atmiņā, un visas izmaiņas, kas tiek piemērotas vienai no tām, neietekmē pārējās.
Labi, jautājums ir šāds: vai tas viss ir saistīts ar burvju izplatīšanās operatora izmantošanu? Šajā gadījumā - jā, bet mūsu modeļiem var būt nepieciešama lielāka sarežģītība nekā šajā piemērā. Ja mēs izmantojam ieliktus objektus, masīvus vai jebkuras citas struktūras, augstākā līmeņa atsauces vērtības izkliedes operators ietekmēs tikai augstāko līmeni, un atsauces īpašībām joprojām būs kopīga vieta atmiņā. Ir daudz risinājumu, kā risināt šo problēmu, viss ir atkarīgs no jūsu vajadzībām. Mēs varam klonēt objektus katrā dziļuma līmenī vai sarežģītākās operācijās izmantot tādus rīkus kā immer, kas ļauj mums gandrīz nesāpīgi rakstīt nemainīgu kodu.
Sajauciet to visu kopā
Vai zināšanas par darbības jomām un vērtību tipiem ir izmantojamas? Protams, ir! Izveidosim kaut ko, kas izmanto abus šos elementus:
const useValue = (defaultValue) => {
const value = [...defaultValue];
const setValue = (newValue) => {
value.length = 0; // viltīgs veids, kā iztīrīt masīvu
newValue.forEach((item, index) => {
value[index] = item;
});
// veikt dažas citas lietas
};
return [value, setValue];
};
const [animals, setAnimals] = useValue(['cat', 'dog']);
console.log(animals); // ['cat', 'dog']
setAnimals([['zirgs', 'govs']]);
console.log(animals); // ['zirgs', 'govs']);
Paskaidrosim, kā šis kods darbojas pa rindiņām. Funkcija useValue izveido masīvu, pamatojoties uz argumentu defaultValue; tā izveido mainīgo un vēl vienu funkciju - tā modifikatoru. Šis modificators iegūst jaunu vērtību, kas tiek sarežģītā veidā piemērota esošajai vērtībai. Funkcijas beigās mēs atgriežam vērtību un tās modifikatoru kā masīva vērtības. Tālāk izmantojam izveidoto funkciju - deklarējam animals un setAnimals kā atgrieztās vērtības. Izmantojam to modificatoru, lai pārbaudītu, vai funkcija ietekmē mainīgo dzīvnieku - jā, tas darbojas!
Bet pagaidiet, kas tieši šajā kodā ir tik iedomāts? Atsauce saglabā visas jaunās vērtības, un šajā modifikatorā var ievietot savu loģiku, piemēram. dažas API vai daļa no ekosistēmas kas nodrošina datu plūsmu bez piepūles. Šis sarežģītais modelis bieži tiek izmantots modernākās JS bibliotēkās, kur funkcionālā programmēšanas paradigma ļauj saglabāt kodu mazāk sarežģītu un citiem programmētājiem vieglāk lasāmu.
Kopsavilkums
Izpratne par to, kā valodas mehānika darbojas zem pārsega, ļauj mums rakstīt apzinātāku un vieglāku kodu. Pat ja JS nav zema līmeņa valoda un liek mums iegūt zināmas zināšanas par to, kā tiek piešķirta un uzglabāta atmiņa, modificējot objektus, mums joprojām ir jāuzmanās no negaidītas uzvedības. No otras puses, vērtību klonu ļaunprātīga izmantošana ne vienmēr ir pareizais veids, un nepareizai izmantošanai ir vairāk mīnusu nekā plusu. Pareizais datu plūsmas plānošanas veids ir apsvērt, kas jums ir nepieciešams un ar kādiem iespējamiem šķēršļiem varat saskarties, īstenojot lietojumprogrammas loģiku.
Lasīt vairāk: