apeescape2.com
  • Glavni
  • Životni Ciklus Proizvoda
  • Mobilni
  • Dizajn Korisničkog Sučelja
  • Uspon Daljinskog Upravljača
Pozadina

Kao JS programer, ovo je ono što me održava noću

JavaScript je čudna jezik. Iako nadahnut Smalltalkom, koristi sintaksu nalik na C. Kombinira aspekte paradigmi proceduralnog, funkcionalnog i objektno orijentiranog programiranja (OOP). Ima brojne , često suvišan, pristupa rješavanju gotovo svih zamislivih problema programiranja i nema snažno mišljenje o tome koji se preferiraju. Slabo je i dinamično tipkan, s labirintnim pristupom prisili tipa, koji pokreće čak i iskusne programere.

JavaScript također ima svoje bradavice, zamke i upitne značajke. Novi programeri bore se s nekim težim konceptima - misle na asinkronost, zatvaranje i podizanje. Programeri s iskustvom na drugim jezicima razumno pretpostavljaju da će stvari sa sličnim imenima i izgledima raditi na isti način u JavaScript-u i često griješe. Nizovi zapravo nisu nizovi; o čemu se radi this, što je prototip, a što new zapravo učiniti?



Problemi s nastavom ES6

Do sada je najgori prijestupnik novost u najnovijoj izdanju JavaScript-a, ECMAScript 6 (ES6): razreda . Neki od razgovora oko predavanja iskreno su alarmantni i otkrivaju duboko ukorijenjeno nerazumijevanje kako jezik zapravo funkcionira:



“JavaScript je napokon a stvaran objektno orijentirani jezik sada kad ima nastavu! '

Ili:



'Predavanja nas oslobađaju razmišljanja o slomljenom JavaScript modelu nasljeđivanja.'

Ili čak:

'Predavanja su sigurniji, lakši pristup stvaranju vrsta u JavaScript-u.'



Ove me izjave ne smetaju jer impliciraju da nešto nije u redu prototipsko nasljeđivanje ; ostavimo te argumente po strani. Ove me izjave smetaju jer niti jedna od njih nije istinita i pokazuju posljedice JavaScript-ovog pristupa 'sve za svakoga' dizajniranju jezika: programeru umanjuje razumijevanje jezika češće nego što omogućuje. Prije nego što nastavim dalje, ilustrirajmo.

JavaScript pop kviz br. 1: Koja je bitna razlika između ovih blokova koda?

function PrototypicalGreeting(greeting = 'Hello', name = 'World') { this.greeting = greeting this.name = name } PrototypicalGreeting.prototype.greet = function() { return `${this.greeting}, ${this.name}!` } const greetProto = new PrototypicalGreeting('Hey', 'folks') console.log(greetProto.greet()) class ClassicalGreeting { constructor(greeting = 'Hello', name = 'World') { this.greeting = greeting this.name = name } greet() { return `${this.greeting}, ${this.name}!` } } const classyGreeting = new ClassicalGreeting('Hey', 'folks') console.log(classyGreeting.greet())

Ovdje je odgovor nema jednog . Oni učinkovito rade isto, samo je pitanje je li korištena sintaksa klase ES6.

c korporacija vs s korporacija vs llc

Istina, drugi je primjer izražajniji. Samo iz tog razloga možete tvrditi da class je lijep dodatak jeziku. Nažalost, problem je malo suptilniji.

JavaScript pop kviz br. 2: što radi sljedeći kod?

function Proto() { this.name = 'Proto' return this; } Proto.prototype.getName = function() { return this.name } class MyClass extends Proto { constructor() { super() this.name = 'MyClass' } } const instance = new MyClass() console.log(instance.getName()) Proto.prototype.getName = function() { return 'Overridden in Proto' } console.log(instance.getName()) MyClass.prototype.getName = function() { return 'Overridden in MyClass' } console.log(instance.getName()) instance.getName = function() { return 'Overridden in instance' } console.log(instance.getName())

Točan odgovor je da se ispisuje na konzolu:

> MyClass > Overridden in Proto > Overridden in MyClass > Overridden in instance

Ako ste netočno odgovorili, ne razumijete što class zapravo jest. Nisi ti kriv. Slično kao Array, class nije jezična značajka, to je sintaktičko mračnjaštvo . Pokušava sakriti prototipski model nasljeđivanja i nespretne idiome koji dolaze s njim, a podrazumijeva da JavaScript radi nešto što nije.

Možda su vam rekli da class je uveden u JavaScript kako bi klasični OOP programeri koji dolaze iz jezika poput Jave bili ugodniji za model nasljeđivanja klase ES6. Ako ti jesu jedan od onih programera, taj vas je primjer vjerojatno užasnuo. Trebalo bi. To pokazuje da JavaScript ima class ključna riječ ne sadrži niti jedno jamstvo koje klasa treba pružiti. Također pokazuje jednu od ključnih razlika u modelu nasljeđivanja prototipa: Prototipi su instance objekta , nije vrste .

Prototipovi naspram klasa

Najvažnija razlika između nasljeđivanja na temelju klase i prototipa je u tome što klasa definira a tip koji se mogu instancirati tijekom izvođenja, dok je prototip sam po sebi objektna instanca.

Dijete razreda ES6 je još jedno tip definicija koja proširuje roditelja novim svojstvima i metodama, koje se zauzvrat mogu pokrenuti u vrijeme izvođenja. Dijete prototipa je još jedan objekt primjer koja roditelju delegira sva svojstva koja nisu implementirana na djetetu.

Napomena: Možda se pitate zašto sam spomenuo metode klase, ali ne i prototipove. To je zato što JavaScript nema koncept metoda. Funkcije su prvi razred u JavaScript-u, a mogu imati svojstva ili biti svojstva drugih objekata.

Konstruktor klase stvara instancu klase. Konstruktor u JavaScript-u samo je obična stara funkcija koja vraća objekt. Jedino što je posebno kod JavaScript konstruktora je to što se, kada se pozove s new ključna riječ, svoj prototip dodjeljuje kao prototip vraćenog objekta. Ako vam to zvuči pomalo zbunjujuće, niste sami - jest i velik je dio zašto se prototipi slabo razumiju.

Da bismo na to stavili doista finu stvar, dijete prototipa nije kopirati njegovog prototipa, niti je objekt s istim oblik kao njegov prototip. Dijete ima živu referencu do prototip, a svako svojstvo prototipa koje ne postoji na djetetu jednosmjerna je referenca na istoimeno svojstvo na prototipu.

Uzmite u obzir sljedeće:

let parent = { foo: 'foo' } let child = { } Object.setPrototypeOf(child, parent) console.log(child.foo) // 'foo' child.foo = 'bar' console.log(child.foo) // 'bar' console.log(parent.foo) // 'foo' delete child.foo console.log(child.foo) // 'foo' parent.foo = 'baz' console.log(child.foo) // 'baz' Napomena: Gotovo nikada ne biste napisali ovakav kôd u stvarnom životu - to je užasna praksa - ali sažeto pokazuje princip.

U prethodnom primjeru, dok child.foo je undefined, referencirano je parent.foo. Čim smo definirali foo na child, child.foo imao vrijednost 'bar', ali parent.foo zadržao svoju izvornu vrijednost. Jednom kad smo delete child.foo opet se odnosi na parent.foo, što znači da kada promijenimo vrijednost roditelja, child.foo odnosi se na novu vrijednost.

Pogledajmo što se upravo dogodilo (u svrhu jasnije ilustracije pretvarat ćemo se da su to Strings a ne nizalni literali, razlika ovdje nije bitna):

Prolazeći kroz prototipni lanac kako bismo pokazali kako se nedostajuće reference rješavaju u JavaScript-u.

Način na koji ovo funkcionira ispod haube, a posebno posebnosti new i this, tema su za neki drugi dan, ali Mozilla jest temeljit članak o JavaScriptovom lancu nasljeđivanja prototipa ako želite pročitati više.

Ključno je što prototipi ne definiraju type; oni su sami instances i oni su promjenjivi tijekom izvođenja, sa svime što podrazumijeva i podrazumijeva.

Još uvijek ste sa mnom? Vratimo se seciranju JavaScript klasa.

JavaScript pop kviz br. 3: Kako primjenjujete privatnost u nastavi?

Naša gornja svojstva prototipa i klase nisu toliko „inkapsulirana“ koliko „nesigurno vise na prozoru“. To bismo trebali popraviti, ali kako?

Ovdje nema primjera koda. Odgovor je da ne možete.

JavaScript nema nikakav koncept privatnosti, ali ima zatvaranja:

function SecretiveProto() { const secret = 'The Class is a lie!' this.spillTheBeans = function() { console.log(secret) } } const blabbermouth = new SecretiveProto() try { console.log(blabbermouth.secret) } catch(e) { // TypeError: SecretiveClass.secret is not defined } blabbermouth.spillTheBeans() // 'The Class is a lie!'

Razumijete li što se upravo dogodilo? Ako ne, ne razumijete zatvaranja. To je u redu, stvarno - oni nisu toliko zastrašujući kao što se pretvaralo, oni su super korisni, a vi biste trebali odvojite malo vremena da naučite o njima .

JavaScript pop kviz br. 4: što je ekvivalent gore navedenom pomoću class Ključna riječ?

Oprostite, ovo je još jedno trik pitanje. U osnovi možete učiniti isto, ali to izgleda ovako:

class SecretiveClass { constructor() { const secret = 'I am a lie!' this.spillTheBeans = function() { console.log(secret) } } looseLips() { console.log(secret) } } const liar = new SecretiveClass() try { console.log(liar.secret) } catch(e) { console.log(e) // TypeError: SecretiveClass.secret is not defined } liar.spillTheBeans() // 'I am a lie!'

Javite mi izgleda li to lakše ili jasnije nego u SecretiveProto. Po mom osobnom gledištu, to je nešto gore - razbija idiomatsku uporabu class deklaracije u JavaScript-u i ne radi baš onako kako biste očekivali da dolazi od, recimo, Jave. To će biti jasno razvidno iz sljedećeg:

JavaScript pop kviz br. 5: Što znači SecretiveClass::looseLips() Čini?

Hajde da vidimo:

try { liar.looseLips() } catch(e) { // ReferenceError: secret is not defined }

Pa ... to je bilo neugodno.

glavna datoteka c ++

JavaScript pop kviz br. 6: Što preferiraju iskusni programeri JavaScript-a - prototipovi ili satovi?

Pogađate, to je još jedno trik pitanje - iskusni programeri JavaScript obično izbjegavaju oboje kad mogu. Evo lijepog načina da gore navedeno učinimo s idiomatskim JavaScriptom:

function secretFactory() { const secret = 'Favor composition over inheritance, `new` is considered harmful, and the end is near!' const spillTheBeans = () => console.log(secret) return { spillTheBeans } } const leaker = secretFactory() leaker.spillTheBeans()

Ovdje se ne radi samo o izbjegavanju urođene ružnoće nasljeđa ili o provođenju inkapsulacije. Razmislite što biste još mogli učiniti s secretFactory i leaker što ne biste mogli lako učiniti s prototipom ili klasom.

Kao prvo, možete ga destrukturirati jer ne morate brinuti o kontekstu this:

const { spillTheBeans } = secretFactory() spillTheBeans() // Favor composition over inheritance, (...)

To je prilično lijepo. Osim izbjegavanja new i this tomfoolery, omogućuje nam upotrebu naših objekata naizmjenično s modulima CommonJS i ES6. Također olakšava sastav:

function spyFactory(infiltrationTarget) { return { exfiltrate: infiltrationTarget.spillTheBeans } } const blackHat = spyFactory(leaker) blackHat.exfiltrate() // Favor composition over inheritance, (...) console.log(blackHat.infiltrationTarget) // undefined (looks like we got away with it)

Klijenti blackHat ne brinite gdje exfiltrate potječe iz, i spyFactory ne mora se petljati sa Function::bind žongliranje kontekstom ili duboko ugniježđena svojstva. Pazite, ne moramo se puno brinuti o this u jednostavnom sinkronom proceduralnom kodu, ali uzrokuje sve vrste problema u asinkronom kodu koje je bolje izbjeći.

Uz malo razmišljanja, spyFactory može se razviti u visoko sofisticirani alat za špijunažu koji može obrađivati ​​sve vrste infiltracijskih ciljeva - ili drugim riječima, fasada .

Naravno da biste to mogli učiniti i s klasom, ili bolje rečeno, asortimanom klasa, koje sve nasljeđuju iz abstract class ili interface ... osim što JavaScript nema nikakav koncept sažetaka ili sučelja.

Vratimo se na zeleniji primjer da vidimo kako bismo to primijenili u tvornici:

function greeterFactory(greeting = 'Hello', name = 'World') { return { greet: () => `${greeting}, ${name}!` } } console.log(greeterFactory('Hey', 'folks').greet()) // Hey, folks!

Možda ste primijetili da su ove tvornice sve jače dok idemo dalje, ali ne brinite - rade isto. Skidaju se kotačići za trening, ljudi!

To je već manje uobičajeno od prototipa ili klasične verzije istog koda. Drugo, učinkovitije postiže inkapsulaciju svojih svojstava. Također, u nekim slučajevima ima nižu memoriju i učinak (možda se na prvi pogled ne čini tako, ali JIT-ov kompajler tiho radi iza kulisa kako bi umanjio dupliciranje i zaključio vrste).

Tako je sigurnije, često je brže i lakše je pisati ovakav kod. Zašto nam opet trebaju satovi? O, naravno, ponovna upotrebljivost. Što se događa ako želimo nesretne i entuzijastične dobrodošlice? Pa, ako koristimo ClassicalGreeting razreda, vjerojatno uskačemo izravno u sanjarenje hijerarhije razreda. Znamo da ćemo morati parameterizirati interpunkciju, pa ćemo malo refaktorizirati i dodati nekoliko djece:

// Greeting class class ClassicalGreeting { constructor(greeting = 'Hello', name = 'World', punctuation = '!') { this.greeting = greeting this.name = name this.punctuation = punctuation } greet() { return `${this.greeting}, ${this.name}${this.punctuation}` } } // An unhappy greeting class UnhappyGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, ' :(') } } const classyUnhappyGreeting = new UnhappyGreeting('Hello', 'everyone') console.log(classyUnhappyGreeting.greet()) // Hello, everyone :( // An enthusiastic greeting class EnthusiasticGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, '!!') } greet() { return super.greet().toUpperCase() } } const greetingWithEnthusiasm = new EnthusiasticGreeting() console.log(greetingWithEnthusiasm.greet()) // HELLO, WORLD!!

To je fin pristup, sve dok se netko ne pojavi i zatraži značajku koja se ne uklapa čisto u hijerarhiju i cijela stvar prestane imati smisla. Stavite pribadaču u tu misao dok pokušavamo napisati istu funkcionalnost s tvornicama:

const greeterFactory = (greeting = 'Hello', name = 'World', punctuation = '!') => ({ greet: () => `${greeting}, ${name}${punctuation}` }) // Makes a greeter unhappy const unhappy = (greeter) => (greeting, name) => greeter(greeting, name, ':(') console.log(unhappy(greeterFactory)('Hello', 'everyone').greet()) // Hello, everyone :( // Makes a greeter enthusiastic const enthusiastic = (greeter) => (greeting, name) => ({ greet: () => greeter(greeting, name, '!!').greet().toUpperCase() }) console.log(enthusiastic(greeterFactory)().greet()) // HELLO, WORLD!!

Nije očito da je ovaj kôd bolji, iako je nešto kraći. Zapravo, mogli biste tvrditi da je teže čitati, a možda je ovo tup pristup. Ne bismo li mogli imati samo unhappyGreeterFactory i an enthusiasticGreeterFactory?

Tada dolazi vaš klijent i kaže: 'Treba mi novi pozdrav koji je nesretan i želi da cijela soba zna za to!'

console.log(enthusiastic(unhappy(greeterFactory))().greet()) // HELLO, WORLD :(

Ako bismo ovaj entuzijastično nesretni pozdrav trebali koristiti više puta, mogli bismo si olakšati:

const aggressiveGreeterFactory = enthusiastic(unhappy(greeterFactory)) console.log(aggressiveGreeterFactory('You're late', 'Jim').greet())

Postoje pristupi ovom stilu kompozicije koji rade s prototipovima ili klasama. Na primjer, možete preispitati UnhappyGreeting i EnthusiasticGreeting kao dekorateri . I dalje bi trebalo više uzorka od prethodno korištenog pristupa u funkcionalnom stilu, ali to je cijena koju plaćate za sigurnost i inkapsulaciju stvaran razreda.

Stvar je u tome što u JavaScript-u ne dobivate onu automatsku sigurnost. JavaScript okviri koji ističu class upotreba čini puno 'magije' da se papiriraju ove vrste problema i prisile klase da se ponašaju. Pogledajte Polymer's ElementMixin izvorni kod neko vrijeme, usudit ću se. To su razine čarobnjaka za arh JavaScript, i mislim na to bez ironije i sarkazma.

Naravno, neke od gore raspravljenih problema možemo popraviti pomoću Object.freeze ili Object.defineProperties s većim ili manjim učinkom. Ali zašto imitirati obrazac bez funkcije, a zanemarujući alate JavaScript čini izvorno nam pružaju da ih možda ne bismo pronašli na jezicima poput Jave? Da li biste koristili čekić s oznakom 'odvijač' za zabijanje vijka, kad je vaš kutija s alatima kao stvarni odvijač sjedio tik do njega?

Pronalaženje dobrih dijelova

Programeri JavaScript često ističu dobre dijelove jezika, kako u kolokvijalnom, tako i u odnosu na istoimena knjiga . Nastojimo izbjeći zamke postavljene upitnijim odabirom jezika i držimo se dijelova koji nam omogućavaju da napišemo čist, čitljiv kôd za ponovnu upotrebu koji smanjuje pogreške.

Postoje opravdani argumenti oko toga koji dijelovi JavaScript ispunjavaju uvjete, ali nadam se da sam vas uvjerio da class nije jedan od njih. Ako to ne uspije, nadamo se da razumijete da nasljeđivanje u JavaScriptu može biti zbunjujući nered i da class niti je popravlja niti štedi zbog razumijevanja prototipova. Dodatni kredit ako ste shvatili nagovještaje da objektno orijentirani uzorci dizajna dobro funkcioniraju bez klasa ili nasljeđivanja ES6.

Ne kažem vam da izbjegavate class u cijelosti. Ponekad vam treba nasljedstvo i class pruža čistiju sintaksu za to. Konkretno, class X extends Y je mnogo ljepši od starog prototipa. Uz to, mnogi popularni front-end okviri potiču njegovu upotrebu i vjerojatno biste trebali izbjegavati pisanje čudnih nestandardnih kodova samo u principu. Jednostavno ne volim kuda ovo ide.

U mojim noćnim morama cijela generacija JavaScript knjižnica napisana je pomoću class, s očekivanjem da će se ponašati slično kao i drugi popularni jezici. Otkrivaju se cijele nove klase bugova (namijenjene igri riječi). Uskrsnuće stari koji bi lako mogli ostati na groblju neispravnog JavaScript-a da nismo neoprezno upali u class zamka. Iskusna JavaScript programera muče ova čudovišta, jer ono što je popularno nije uvijek ono što je dobro.

Na kraju svi frustrirano odustanemo i počnemo izmišljati kotače u Rustu, Gou, Haskellu ili tko zna čemu još, a zatim se kompiliramo za Wasm za web, a novi mrežni okviri i knjižnice rastu u višejezičnu beskonačnost.

Stvarno me drži budnim noću.

Razumijevanje osnova

Što je ECMAScript 6?

ES6 je najnovija stabilna implementacija ECMAScripta, otvorenog standarda na kojem se temelji JavaScript. Jeziku dodaje brojne nove značajke, uključujući službeni sustav modula, varijable i konstante s opsegom bloka, funkcije strelica i brojne druge nove ključne riječi, sintakse i ugrađene objekte.

Je li ES6 najnovija verzija ECMAScripta?

ES6 (ES2015) najnoviji je standard koji je stabilan i u potpunosti se implementira (osim za odgovarajuće rep pozive i neke nijanse) u najnovijim verzijama glavnih preglednika i drugih JS okruženja. ES7 (ES2016) i ES8 (ES2017) također su stabilne specifikacije, ali provedba je prilično mješovita.

kako funkcioniraju konvertibilne note

Je li ES6 objektno orijentiran?

JavaScript ima snažnu podršku objektno orijentiranom programiranju, ali koristi drugačiji model nasljeđivanja (prototipski) u usporedbi s većinom popularnih OO jezika (koji koriste klasično nasljeđivanje). Također podržava proceduralne i funkcionalne stilove programiranja.

Što su ES6 razredi?

U ES6, ključna riječ 'class' i pridružene značajke novi su pristup stvaranju prototipskih konstruktora. Oni nisu istinske klase na način koji bi bio poznat korisnicima većine drugih objektno orijentiranih jezika.

Koje se ključne riječi mogu koristiti za implementaciju nasljeđivanja u ES6?

Nasljeđivanje se može implementirati u JavaScript ES6 putem ključnih riječi 'class' i 'extends'. Sljedeći je pristup putem idioma funkcije 'konstruktor', plus dodjela funkcija i statičkih svojstava prototipu konstruktora.

Što je nasljeđivanje prototipa u JavaScript-u?

U prototipskom nasljeđivanju, prototipovi su objektne instance kojima podređene instance delegiraju nedefinirana svojstva. Suprotno tome, klase u klasičnom nasljeđivanju definicije su tipa iz kojih podređene klase nasljeđuju metode i svojstva tijekom instanciranja.

Šef zajednice

Ostalo

Šef zajednice
Popis najboljih besplatnih knjiga o programiranju na ApeeScapeu

Popis najboljih besplatnih knjiga o programiranju na ApeeScapeu

Tehnologija

Popularni Postovi
3D vizualizacija podataka s alatima otvorenog koda: Vodič kroz VTK
3D vizualizacija podataka s alatima otvorenog koda: Vodič kroz VTK
Cjelovit vodič za UX metode istraživanja
Cjelovit vodič za UX metode istraživanja
Shopify dizajnerski savjeti i UX najbolji primjeri iz prakse
Shopify dizajnerski savjeti i UX najbolji primjeri iz prakse
Kako implementirati T9 Search u iOS
Kako implementirati T9 Search u iOS
Kako savjetnici za slobodne financije pobjeđuju velike tvrtke
Kako savjetnici za slobodne financije pobjeđuju velike tvrtke
 
Razvoj API-ja u programu Go Goa
Razvoj API-ja u programu Go Goa
Pojednostavljena integracija softvera: Vodič za Apache Camel
Pojednostavljena integracija softvera: Vodič za Apache Camel
Vlak za izdavanje Salesforcea: Praktični pristup upravljanju izdanjima
Vlak za izdavanje Salesforcea: Praktični pristup upravljanju izdanjima
Direktor Poklapanja talenata
Direktor Poklapanja talenata
Vodič za ambiciozne programere Google Stakla: Izgradnja vaše prve aplikacije za staklo
Vodič za ambiciozne programere Google Stakla: Izgradnja vaše prve aplikacije za staklo
Popularni Postovi
  • najbolje web mjesto za učenje c ++
  • malina pi 3 poslužiteljski projekti
  • na kojem je programskom jeziku napisan linux
  • hakirana kreditna kartica s saldom 2020
  • c ++ uključuje datoteku
Kategorije
Alati I Vodiči Ui Dizajn Savjeti I Alati Tehnologija Životni Stil Planiranje I Predviđanje Investitori I Financiranje Život Dizajnera Ux Dizajn Trendovi

© 2021 | Sva Prava Pridržana

apeescape2.com