Skip to content
Natuurondernemer
    Febbraio 19, 2021 by admin

    Un principiante' Guida a JavaScript's Prototype

    Un principiante' Guida a JavaScript's Prototype
    Febbraio 19, 2021 by admin

    Non si può andare molto lontano in JavaScript senza avere a che fare con gli oggetti. Sono fondamentali per quasi ogni aspetto del linguaggio di programmazione JavaScript. Infatti, imparare a creare oggetti è probabilmente una delle prime cose che avete studiato quando avete iniziato. Detto questo, per imparare nel modo più efficace i prototipi in JavaScript, canalizzeremo il nostro sviluppatore junior interiore e torneremo alle basi.

    Gli oggetti sono coppie chiave/valore. Il modo più comune per creare un oggetto è con le parentesi graffe {} e si aggiungono proprietà e metodi ad un oggetto usando la notazione per punti.

    let animal = {}animal.name = 'Leo'animal.energy = 10animal.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}animal.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}animal.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}

    Semplice. Ora è probabile che nella nostra applicazione avremo bisogno di creare più di un animale. Naturalmente, il passo successivo sarebbe quello di incapsulare questa logica all’interno di una funzione che possiamo invocare ogni volta che abbiamo bisogno di creare un nuovo animale. Chiameremo questo schema Functional Instantiation e chiameremo la funzione stessa una “funzione costruttrice” poiché è responsabile della “costruzione” di un nuovo oggetto.

    Istanziazione funzionale

    function Animal (name, energy) { let animal = {} animal.name = name animal.energy = energy animal.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount } animal.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length } animal.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length } return animal}const leo = Animal('Leo', 7)const snoop = Animal('Snoop', 10)

    "I thought this was an Advanced JavaScript course...?" - Your brain Si. Ci arriveremo.

    Ora, ogni volta che vogliamo creare un nuovo animale (o più in generale una nuova “istanza”), tutto quello che dobbiamo fare è invocare la nostra funzione Animal, passandole il livello name e energy dell’animale. Questo funziona benissimo ed è incredibilmente semplice. Tuttavia, puoi individuare qualche punto debole di questo modello? Il più grande e quello che cercheremo di risolvere ha a che fare con i tre metodi – eatsleep, e play. Ognuno di questi metodi non solo è dinamico, ma è anche completamente generico. Ciò significa che non c’è motivo di ricreare questi metodi come stiamo facendo attualmente ogni volta che creiamo un nuovo animale. Stiamo solo sprecando memoria e rendendo ogni oggetto animale più grande del necessario. Potete pensare ad una soluzione? E se invece di ricreare questi metodi ogni volta che creiamo un nuovo animale, li spostassimo in un proprio oggetto e potessimo fare in modo che ogni animale faccia riferimento a quell’oggetto? Possiamo chiamare questo pattern Functional Instantiation with Shared Methods, verboso ma descrittivo.

    Istanziazione funzionale con metodi condivisi

    const animalMethods = { eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount }, sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length }, play(length) { console.log(`${this.name} is playing.`) this.energy -= length }}function Animal (name, energy) { let animal = {} animal.name = name animal.energy = energy animal.eat = animalMethods.eat animal.sleep = animalMethods.sleep animal.play = animalMethods.play return animal}const leo = Animal('Leo', 7)const snoop = Animal('Snoop', 10)

    Spostando i metodi condivisi nel proprio oggetto e referenziando tale oggetto all’interno della nostra funzione Animal, abbiamo risolto il problema dello spreco di memoria e degli oggetti animali troppo grandi.

    Object.create

    Miglioriamo ancora una volta il nostro esempio usando Object.create. In parole povere, Object.create vi permette di creare un oggetto che delegherà ad un altro oggetto su ricerche fallite. In altre parole, Object.create vi permette di creare un oggetto e ogni volta che c’è una ricerca di proprietà fallita su quell’oggetto, può consultare un altro oggetto per vedere se quell’altro oggetto ha la proprietà. Sono state un sacco di parole. Vediamo un po’ di codice.

    const parent = { name: 'Stacey', age: 35, heritage: 'Irish'}const child = Object.create(parent)child.name = 'Ryan'child.age = 7console.log(child.name) // Ryanconsole.log(child.age) // 7console.log(child.heritage) // Irish

    Così nell’esempio sopra, poiché child è stato creato con Object.create(parent), ogni volta che c’è una ricerca di proprietà fallita su child, JavaScript delegherà quella ricerca all’oggetto parent. Ciò significa che anche se child non ha una proprietà heritageparent ce l’ha, quindi quando si registra child.heritage si ottiene l’eredità di parent che era Irish.

    Ora con Object.create nel nostro capanno degli attrezzi, come possiamo usarlo per semplificare il nostro codice Animal di prima? Bene, invece di aggiungere tutti i metodi condivisi all’animale uno per uno come stiamo facendo ora, possiamo usare Object.create per delegare all’oggetto animalMethods. Per sembrare davvero intelligenti, chiamiamolo Functional Instantiation with Shared Methods and Object.create 🙃

    Istanziazione funzionale con metodi condivisi e Object.create

    const animalMethods = { eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount }, sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length }, play(length) { console.log(`${this.name} is playing.`) this.energy -= length }}function Animal (name, energy) { let animal = Object.create(animalMethods) animal.name = name animal.energy = energy return animal}const leo = Animal('Leo', 7)const snoop = Animal('Snoop', 10)leo.eat(10)snoop.play(5)

    📈 Così ora quando chiamiamo leo.eat, JavaScript cercherà il metodo eat sull’oggetto leo. Quella ricerca fallirà, quindi, a causa di Object.create, delegherà all’oggetto animalMethods che è dove troverà eat.

    Fin qui, tutto bene. Ci sono ancora alcuni miglioramenti che possiamo fare. Sembra un po’ “hacky” dover gestire un oggetto separato (animalMethods) per condividere i metodi tra le istanze. Sembra una caratteristica comune che si vorrebbe implementare nel linguaggio stesso. Si scopre che lo è ed è l’intera ragione per cui siete qui – prototype.

    Cos’è esattamente prototype in JavaScript? Beh, semplicemente, ogni funzione in JavaScript ha una proprietà prototype che fa riferimento ad un oggetto. Anticlimatico, vero? Provate voi stessi.

    function doThing () {}console.log(doThing.prototype) // {}

    E se invece di creare un oggetto separato per gestire i nostri metodi (come stiamo facendo con animalMethods), mettessimo semplicemente ognuno di quei metodi sul prototipo della funzione Animal? Allora tutto quello che dovremmo fare è invece di usare Object.create per delegare a animalMethods, potremmo usarlo per delegare a Animal.prototype. Chiameremo questo pattern Prototypal Instantiation.

    Istanziazione prototipale

    function Animal (name, energy) { let animal = Object.create(Animal.prototype) animal.name = name animal.energy = energy return animal}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = Animal('Leo', 7)const snoop = Animal('Snoop', 10)leo.eat(10)snoop.play(5)

    👏👏👏👏 Speriamo che tu abbia appena avuto un grande momento “aha”. Di nuovo, prototype è solo una proprietà che ogni funzione in JavaScript ha e, come abbiamo visto sopra, ci permette di condividere i metodi tra tutte le istanze di una funzione. Tutte le nostre funzionalità sono ancora le stesse, ma ora invece di dover gestire un oggetto separato per tutti i metodi, possiamo semplicemente usare un altro oggetto che viene costruito nella Animal funzione stessa, Animal.prototype.

    Andiamo. Andare. Deeper.

    A questo punto sappiamo tre cose:

    1. Come creare una funzione costruttore.
    2. Come aggiungere metodi al prototipo della funzione costruttore.
    3. Come usare Object.create per delegare le ricerche fallite al prototipo della funzione.

    Questi tre compiti sembrano piuttosto fondamentali per qualsiasi linguaggio di programmazione. JavaScript è davvero così male che non c’è un modo più facile, “incorporato” per realizzare la stessa cosa? Come potete probabilmente indovinare a questo punto c’è, ed è usando la parola chiave new.

    Quello che è bello dell’approccio lento e metodico che abbiamo preso per arrivare qui è che ora avrete una profonda comprensione di esattamente quello che la parola chiave new in JavaScript sta facendo sotto il cappuccio.

    Guardando indietro al nostro Animal costruttore, le due parti più importanti erano la creazione dell’oggetto e la sua restituzione. Senza la creazione dell’oggetto con Object.create, non saremmo in grado di delegare al prototipo della funzione su ricerche fallite. Senza la dichiarazione return, non avremmo mai indietro l’oggetto creato.

    function Animal (name, energy) { let animal = Object.create(Animal.prototype) animal.name = name animal.energy = energy return animal}

    Ecco la cosa bella di new – quando si invoca una funzione usando la parola chiave new, quelle due righe sono fatte per voi implicitamente (“sotto il cofano”) e l’oggetto che viene creato si chiama this.

    Usando i commenti per mostrare cosa succede sotto il cofano e assumendo che il Animal costruttore sia chiamato con la new parola chiave, può essere riscritto così.

    function Animal (name, energy) { // const this = Object.create(Animal.prototype) this.name = name this.energy = energy // return this}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)

    e senza i commenti “sotto il cappuccio”

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)

    Ancora una volta il motivo per cui questo funziona e che l’oggetto this viene creato per noi è perché abbiamo chiamato la funzione costruttore con la parola chiave new. Se tralasciamo new quando invochiamo la funzione, quell’oggetto this non viene mai creato né restituito implicitamente. Possiamo vedere il problema nell’esempio qui sotto.

    function Animal (name, energy) { this.name = name this.energy = energy}const leo = Animal('Leo', 7)console.log(leo) // undefined

    Il nome di questo pattern è Pseudoclassical Instantiation.

    Se JavaScript non è il tuo primo linguaggio di programmazione, potresti essere un po’ irrequieto.

    “WTF questo tizio ha appena ricreato una versione più schifosa di una Classe” – Tu

    Per chi non lo sapesse, una Classe permette di creare un modello di un oggetto. Poi, ogni volta che si crea un’istanza di quella Classe, si ottiene un oggetto con le proprietà e i metodi definiti nel blueprint.

    Suona familiare? Questo è fondamentalmente quello che abbiamo fatto con la nostra funzione Animal costruttore di cui sopra. Tuttavia, invece di usare la parola chiave class, abbiamo semplicemente usato una vecchia funzione JavaScript per ricreare la stessa funzionalità. Certo, c’è voluto un po’ di lavoro in più e un po’ di conoscenza di ciò che accade “sotto il cofano” di JavaScript, ma i risultati sono gli stessi.

    Ecco la buona notizia. JavaScript non è un linguaggio morto. Viene costantemente migliorato e aggiunto dal comitato TC-39. Ciò significa che anche se la versione iniziale di JavaScript non supportava le classi, non c’è ragione per cui esse non possano essere aggiunte alla specifica ufficiale. Infatti, questo è esattamente ciò che il comitato TC-39 ha fatto. Nel 2015, EcmaScript (la specifica ufficiale di JavaScript) 6 è stato rilasciato con il supporto per le classi e la parola chiave class. Vediamo come la nostra Animal funzione costruttore di cui sopra apparirebbe con la nuova sintassi di classe.

    class Animal { constructor(name, energy) { this.name = name this.energy = energy } eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount } sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length } play(length) { console.log(`${this.name} is playing.`) this.energy -= length }}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)

    Piuttosto pulito, giusto?

    Se questo è il nuovo modo di creare le classi, perché abbiamo speso così tanto tempo a rivedere il vecchio modo? Il motivo è che il nuovo modo (con la parola chiave class) è principalmente solo “zucchero sintattico” sopra il modo esistente che abbiamo chiamato pattern pseudo-classico. Per comprendere appieno la sintassi di convenienza delle classi ES6, dovete prima capire il pattern pseudo-classico.

    A questo punto abbiamo coperto i fondamenti del prototipo di JavaScript. Il resto di questo post sarà dedicato alla comprensione di altri argomenti “buoni da sapere” relativi ad esso. In un altro post, vedremo come possiamo prendere questi fondamenti e usarli per capire come funziona l’ereditarietà in JavaScript.

    Metodi Array

    Abbiamo parlato in modo approfondito sopra di come se si vogliono condividere metodi tra le istanze di una classe, si dovrebbero attaccare questi metodi al prototipo della classe (o della funzione). Possiamo vedere questo stesso schema dimostrato se guardiamo la classe Array. Storicamente avete probabilmente creato i vostri array in questo modo

    const friends = 

    Si scopre che questo è solo zucchero sulla creazione di un’istanza new della classe Array.

    const friendsWithSugar = const friendsWithoutSugar = new Array()

    Una cosa a cui forse non avete mai pensato è come fa ogni istanza di un array ad avere tutti quei metodi built-in (spliceslicepop, etc)?

    Bene, come ora sapete, è perché questi metodi vivono su Array.prototype e quando create una nuova istanza di Array, usate la parola chiave new che imposta la delega a Array.prototype su ricerche fallite.

    Possiamo vedere tutti i metodi dell’array semplicemente registrando Array.prototype.

    console.log(Array.prototype)/* concat: ƒn concat() constructor: ƒn Array() copyWithin: ƒn copyWithin() entries: ƒn entries() every: ƒn every() fill: ƒn fill() filter: ƒn filter() find: ƒn find() findIndex: ƒn findIndex() forEach: ƒn forEach() includes: ƒn includes() indexOf: ƒn indexOf() join: ƒn join() keys: ƒn keys() lastIndexOf: ƒn lastIndexOf() length: 0n map: ƒn map() pop: ƒn pop() push: ƒn push() reduce: ƒn reduce() reduceRight: ƒn reduceRight() reverse: ƒn reverse() shift: ƒn shift() slice: ƒn slice() some: ƒn some() sort: ƒn sort() splice: ƒn splice() toLocaleString: ƒn toLocaleString() toString: ƒn toString() unshift: ƒn unshift() values: ƒn values()*/

    La stessa logica esiste anche per gli oggetti. Tutti gli oggetti delegheranno a Object.prototype su ricerche fallite che è il motivo per cui tutti gli oggetti hanno metodi come toString e hasOwnProperty.

    Metodi statici

    Fino a questo punto abbiamo coperto il perché e il come di condividere metodi tra istanze di una Classe. Tuttavia, cosa succede se abbiamo un metodo che è importante per la classe, ma che non ha bisogno di essere condiviso tra le istanze? Per esempio, cosa succederebbe se avessimo una funzione che prende un array di Animal istanze e determina quale deve essere alimentato successivamente? La chiameremo nextToEat.

    function nextToEat (animals) { const sortedByLeastEnergy = animals.sort((a,b) => { return a.energy - b.energy }) return sortedByLeastEnergy.name}

    Non ha senso avere nextToEat in Animal.prototype poiché non vogliamo condividerlo tra tutte le istanze. Invece, possiamo pensare ad esso come ad un metodo di aiuto. Quindi se nextToEat non dovrebbe vivere su Animal.prototype, dove dovremmo metterlo? Beh, la risposta più ovvia è che potremmo semplicemente infilare nextToEat nello stesso scope della nostra classe Animal e fare riferimento ad essa quando ne abbiamo bisogno come faremmo normalmente.

    class Animal { constructor(name, energy) { this.name = name this.energy = energy } eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount } sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length } play(length) { console.log(`${this.name} is playing.`) this.energy -= length }}function nextToEat (animals) { const sortedByLeastEnergy = animals.sort((a,b) => { return a.energy - b.energy }) return sortedByLeastEnergy.name}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)console.log(nextToEat()) // Leo

    Questo funziona, ma c’è un modo migliore.

    Ogni volta che avete un metodo che è specifico di una classe stessa ma che non ha bisogno di essere condiviso tra le istanze di quella classe, potete aggiungerlo come una static proprietà della classe.

    class Animal { constructor(name, energy) { this.name = name this.energy = energy } eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount } sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length } play(length) { console.log(`${this.name} is playing.`) this.energy -= length } static nextToEat(animals) { const sortedByLeastEnergy = animals.sort((a,b) => { return a.energy - b.energy }) return sortedByLeastEnergy.name }}

    Ora, poiché abbiamo aggiunto nextToEat come proprietà static della classe, vive sulla classe Animal stessa (non sul suo prototipo) e vi si può accedere usando Animal.nextToEat.

    const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)console.log(Animal.nextToEat()) // Leo

    Perché abbiamo seguito uno schema simile in tutto questo post, diamo un’occhiata a come potremmo realizzare questa stessa cosa usando ES5. Nell’esempio precedente abbiamo visto come usando la parola chiave static metteremo il metodo direttamente nella classe stessa. Con ES5, questo stesso schema è semplice come aggiungere manualmente il metodo all’oggetto funzione.

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}Animal.nextToEat = function (animals) { const sortedByLeastEnergy = animals.sort((a,b) => { return a.energy - b.energy }) return sortedByLeastEnergy.name}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)console.log(Animal.nextToEat()) // Leo

    Prelevare il prototipo di un oggetto

    Indifferentemente da qualsiasi schema usato per creare un oggetto, ottenere il prototipo di quell’oggetto può essere realizzato usando il metodo Object.getPrototypeOf.

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = new Animal('Leo', 7)const prototype = Object.getPrototypeOf(leo)console.log(prototype)// {constructor: ƒ, eat: ƒ, sleep: ƒ, play: ƒ}prototype === Animal.prototype // true

    Ci sono due cose importanti da prendere dal codice sopra.

    Primo, noterete che è un oggetto con 4 metodi, constructoreatsleep e play. Questo ha senso. Abbiamo usato getPrototypeOf passando l’istanza, leo ottenendo indietro il prototipo dell’istanza, che è dove vivono tutti i nostri metodi. Questo ci dice anche un’altra cosa su prototype di cui non abbiamo ancora parlato. Per default, l’oggetto prototype avrà una proprietà constructor che punta alla funzione originale o alla classe da cui è stata creata l’istanza. Ciò significa anche che, poiché JavaScript mette una proprietà constructor sul prototipo per impostazione predefinita, qualsiasi istanza sarà in grado di accedere al suo costruttore tramite instance.constructor.

    Il secondo importante risultato di cui sopra è che Object.getPrototypeOf(leo) === Animal.prototype. Anche questo ha senso. La funzione Animal costruttore ha una proprietà prototipo dove possiamo condividere i metodi tra tutte le istanze e getPrototypeOf ci permette di vedere il prototipo dell’istanza stessa.

    function Animal (name, energy) { this.name = name this.energy = energy}const leo = new Animal('Leo', 7)console.log(leo.constructor) // Logs the constructor function

    Per collegare ciò di cui abbiamo parlato prima con Object.create, il motivo per cui questo funziona è che qualsiasi istanza di Animal delegherà a Animal.prototype su ricerche fallite. Così, quando si cerca di accedere a leo.constructorleo non ha una proprietà constructor e delegherà la ricerca a Animal.prototype che ha effettivamente una proprietà constructor. Se questo paragrafo non ha senso, tornate indietro e leggete il precedente Object.create.

    Potreste aver visto __proto__ usato prima per ottenere il prototipo di un’istanza. Questa è una reliquia del passato. Invece, usate Object.getPrototypeOf(instance) come abbiamo visto sopra.

    Determinare se una proprietà vive sul prototipo

    Ci sono alcuni casi in cui avete bisogno di sapere se una proprietà vive sull’istanza stessa o se vive sul prototipo a cui l’oggetto delega. Possiamo vederlo in azione facendo un looping sul nostro oggetto leo che abbiamo creato. Diciamo che l’obiettivo era quello di fare un ciclo su leo e registrare tutte le sue chiavi e valori. Usando un ciclo for in, probabilmente assomiglierebbe a questo.

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = new Animal('Leo', 7)for(let key in leo) { console.log(`Key: ${key}. Value: ${leo}`)}

    Cosa vi aspettereste di vedere? Molto probabilmente, qualcosa di simile a questo –

    Key: name. Value: LeoKey: energy. Value: 7

    Tuttavia, quello che avete visto se avete eseguito il codice è stato questo –

    Key: name. Value: LeoKey: energy. Value: 7Key: eat. Value: function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Key: sleep. Value: function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Key: play. Value: function (length) { console.log(`${this.name} is playing.`) this.energy -= length}

    Perché? Beh, un for in loop sta per eseguire un ciclo su tutte le proprietà enumerabili sia sull’oggetto stesso che sul prototipo che delega. Poiché per default ogni proprietà aggiunta al prototipo della funzione è enumerabile, vediamo non solo name e energy, ma vediamo anche tutti i metodi sul prototipo – eatsleep, e play. Per risolvere questo problema, dobbiamo specificare che tutti i metodi del prototipo non sono enumerabili o abbiamo bisogno di un modo per console.log solo se la proprietà è sull’oggetto leo stesso e non sul prototipo che leo delega su ricerche fallite. È qui che hasOwnProperty può aiutarci.

    hasOwnProperty è una proprietà su ogni oggetto che restituisce un booleano che indica se l’oggetto ha la proprietà specificata come sua proprietà piuttosto che sul prototipo a cui l’oggetto delega. Questo è esattamente ciò di cui abbiamo bisogno. Ora con questa nuova conoscenza, possiamo modificare il nostro codice per sfruttare hasOwnProperty all’interno del nostro for in loop.

    ...const leo = new Animal('Leo', 7)for(let key in leo) { if (leo.hasOwnProperty(key)) { console.log(`Key: ${key}. Value: ${leo}`) }}

    E ora quello che vediamo sono solo le proprietà che sono sull’oggetto leo stesso piuttosto che sul prototipo leo delegato anch’esso.

    Key: name. Value: LeoKey: energy. Value: 7

    Se siete ancora un po’ confusi sul hasOwnProperty, ecco un po’ di codice che potrebbe chiarirvi.

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = new Animal('Leo', 7)leo.hasOwnProperty('name') // trueleo.hasOwnProperty('energy') // trueleo.hasOwnProperty('eat') // falseleo.hasOwnProperty('sleep') // falseleo.hasOwnProperty('play') // false

    Controlla se un oggetto è un’istanza di una classe

    A volte vuoi sapere se un oggetto è un’istanza di una classe specifica. Per farlo, potete usare l’operatore instanceof. Il caso d’uso è abbastanza semplice, ma la sintassi attuale è un po’ strana se non l’avete mai vista prima. Funziona così

    object instanceof Class

    La dichiarazione di cui sopra restituirà true se object è un’istanza di Class e false se non lo è. Tornando al nostro esempio Animal avremmo qualcosa del genere.

    function Animal (name, energy) { this.name = name this.energy = energy}function User () {}const leo = new Animal('Leo', 7)leo instanceof Animal // trueleo instanceof User // false

    Il modo in cui instanceof funziona è controllare la presenza di constructor.prototype nella catena dei prototipi dell’oggetto. Nell’esempio sopra, leo instanceof Animaltrue perché Object.getPrototypeOf(leo) === Animal.prototype. Inoltre, leo instanceof Userfalse perché Object.getPrototypeOf(leo) !== User.prototype.

    Creazione di nuove funzioni costruttore agnostiche

    Si può individuare l’errore nel codice sottostante?

    function Animal (name, energy) { this.name = name this.energy = energy}const leo = Animal('Leo', 7)

    Anche gli sviluppatori esperti di JavaScript a volte si imbattono nell’esempio precedente. Poiché stiamo usando il pseudoclassical pattern che abbiamo imparato prima, quando la funzione Animal costruttore viene invocata, dobbiamo assicurarci di invocarla con la parola chiave new. Se non lo facciamo, allora la parola chiave this non verrà creata e non verrà nemmeno restituita implicitamente.

    Come aggiornamento, le linee commentate sono ciò che accade dietro le quinte quando si usa la parola chiave new su una funzione.

    function Animal (name, energy) { // const this = Object.create(Animal.prototype) this.name = name this.energy = energy // return this}

    Questo sembra un dettaglio troppo importante per lasciarlo ricordare agli altri sviluppatori. Supponendo di lavorare in un team con altri sviluppatori, c’è un modo per assicurarsi che il nostro Animal costruttore sia sempre invocato con la new parola chiave? Si scopre che c’è ed è usando l’operatore instanceof che abbiamo imparato in precedenza.

    Se il costruttore è stato chiamato con la parola chiave new, allora this all’interno del corpo del costruttore sarà un instanceof la funzione costruttore stessa. Sono stati un sacco di paroloni. Ecco un po’ di codice.

    function Animal (name, energy) { if (this instanceof Animal === false) { console.warn('Forgot to call Animal with the new keyword') } this.name = name this.energy = energy}

    Ora invece di registrare solo un avvertimento al consumatore della funzione, cosa succede se re-invochiamo la funzione ma con la parola chiave new questa volta?

    function Animal (name, energy) { if (this instanceof Animal === false) { return new Animal(name, energy) } this.name = name this.energy = energy}

    Ora, indipendentemente dal fatto che Animal sia invocato con la parola chiave new, funzionerà ancora correttamente.

    Ricreare Object.create

    In tutto questo post, abbiamo fatto molto affidamento su Object.create per creare oggetti che delegano al prototipo della funzione costruttore. A questo punto, dovreste sapere come usare Object.create all’interno del vostro codice, ma una cosa a cui potreste non aver pensato è come Object.create funzioni effettivamente sotto il cofano. Per farvi capire veramente come funziona Object.create, lo ricreeremo noi stessi. Prima di tutto, cosa sappiamo di come funziona Object.create?

    1. Prende un argomento che è un oggetto.
    2. Crea un oggetto che delega all’oggetto argomento su ricerche fallite.
    3. Ritorna il nuovo oggetto creato.

    Partiamo da #1.

    Object.create = function (objToDelegateTo) {}

    Semplice abbastanza.

    Ora #2 – dobbiamo creare un oggetto che deleghi all’oggetto argomento su ricerche fallite. Questo è un po’ più complicato. Per farlo, useremo la nostra conoscenza di come la parola chiave new e i prototipi funzionano in JavaScript. Per prima cosa, all’interno del corpo della nostra implementazione Object.create, creeremo una funzione vuota. Poi, imposteremo il prototipo di quella funzione vuota uguale all’oggetto argomento. Poi, per creare un nuovo oggetto, invocheremo la nostra funzione vuota usando la parola chiave new. Se restituiamo l’oggetto appena creato, anche questo finirà #3.

    Object.create = function (objToDelegateTo) { function Fn(){} Fn.prototype = objToDelegateTo return new Fn()}

    Wild. Quando creiamo una nuova funzione, Fn nel codice sopra, essa viene fornita con una proprietà prototype. Quando la invochiamo con la parola chiave new, sappiamo che ciò che otterremo indietro è un oggetto che delegherà al prototipo della funzione su ricerche fallite. Se sovrascriviamo il prototipo della funzione, allora possiamo decidere a quale oggetto delegare su ricerche fallite. Quindi nel nostro esempio sopra, sovrascriviamo il prototipo di Fn con l’oggetto che è stato passato quando Object.create è stato invocato, che chiamiamo objToDelegateTo.

    Nota che stiamo supportando un solo argomento a Object.create. L’implementazione ufficiale supporta anche un secondo argomento opzionale che permette di aggiungere più proprietà all’oggetto creato.

    Funzioni freccia

    Le funzioni freccia non hanno una propria this parola chiave. Di conseguenza, le funzioni freccia non possono essere funzioni costruttrici e se si cerca di invocare una funzione freccia con la parola chiave new, verrà lanciato un errore.

    const Animal = () => {}const leo = new Animal() // Error: Animal is not a constructor

    Inoltre, poiché abbiamo dimostrato sopra che il pattern pseudo-classico non può essere usato con le funzioni freccia, le funzioni freccia non hanno nemmeno una proprietà prototype.

    const Animal = () => {}console.log(Animal.prototype) // undefined

    Previous articleDIRECTORYNext article Ghiandole paratiroidi: Fatti, funzione e malattia

    Lascia un commento Annulla risposta

    Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

    Articoli recenti

    • Trovare se stessi (e gli altri…) negli annuari online
    • Come impostare un bitcoin ASIC miner
    • Cos’è un sito Superfund?
    • I vermi sanguigni con esca da pesca hanno morsi di api
    • Ecolalia: I fatti oltre il “parlare a pappagallo”, lo scripting e l’eco
    • Citazioni del Signore delle Mosche
    • A Beginner’s Guide to Pegging
    • 42 ricette sane di zuppa Crockpot
    • 3 rischi sorprendenti della cattiva postura
    • Pesce Betta femmina

    Archivi

    • Aprile 2021
    • Marzo 2021
    • Febbraio 2021
    • Gennaio 2021
    • Dicembre 2020
    • Novembre 2020
    • Ottobre 2020
    • Settembre 2020
    • Agosto 2020
    • Luglio 2020
    • Giugno 2020
    • Maggio 2020
    • Aprile 2020
    • DeutschDeutsch
    • NederlandsNederlands
    • EspañolEspañol
    • FrançaisFrançais
    • PortuguêsPortuguês
    • ItalianoItaliano
    • PolskiPolski

    Meta

    • Accedi
    • Feed dei contenuti
    • Feed dei commenti
    • WordPress.org
    Posterity WordPress Theme