Eredità prototipale
Ad eccezione di null
e undefined
, ogni tipo di dati primitivi ha una
prototype, un wrapper oggetto corrispondente che fornisce i metodi per lavorare
con valori. Quando una ricerca di metodi o proprietà viene richiamata su una primitiva,
JavaScript racchiude la primitiva dietro le quinte e chiama il metodo o
esegue invece la ricerca della proprietà
sull'oggetto wrapper.
Ad esempio, un valore letterale stringa non ha metodi propri, ma puoi richiamare il metodo
.toUpperCase()
su di esso grazie all'oggetto String
corrispondente
wrapper:
"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL
Questa si chiama ereditarietà prototipale ed eredita proprietà e metodi. dal costruttore corrispondente di un valore.
Number.prototype
> Number { 0 }
> constructor: function Number()
> toExponential: function toExponential()
> toFixed: function toFixed()
> toLocaleString: function toLocaleString()
> toPrecision: function toPrecision()
> toString: function toString()
> valueOf: function valueOf()
> <prototype>: Object { … }
Puoi creare le primitive utilizzando questi costruttori, anziché limitarti a definire
in base al loro valore. Ad esempio, l'utilizzo del costruttore String
crea una
oggetto stringa, non un valore letterale stringa: un oggetto che non contiene solo la nostra stringa
ma tutte le proprietà e i metodi ereditati del costruttore.
const myString = new String( "I'm a string." );
myString;
> String { "I'm a string." }
typeof myString;
> "object"
myString.valueOf();
> "I'm a string."
Per la maggior parte, gli oggetti risultanti si comportano come i valori che abbiamo utilizzato
che li definisci. Ad esempio, anche se definisci un valore numerico utilizzando
Il costruttore new Number
restituisce un oggetto contenente tutti i metodi e
proprietà del prototipo Number
, puoi utilizzare operatori matematici
per quegli oggetti proprio come faresti per i valori letterali numerici:
const numberOne = new Number(1);
const numberTwo = new Number(2);
numberOne;
> Number { 1 }
typeof numberOne;
> "object"
numberTwo;
> Number { 2 }
typeof numberTwo;
> "object"
numberOne + numberTwo;
> 3
Sarà molto raro dover usare questi costruttori, perché il codice JavaScript l'ereditarietà prototipale significa che non forniscono alcun vantaggio pratico. Creazione in corso... le primitive che usano i costruttori possono anche portare a risultati imprevisti, poiché il risultato è un oggetto, non un semplice valore letterale:
let stringLiteral = "String literal."
typeof stringLiteral;
> "string"
let stringObject = new String( "String object." );
stringObject
> "object"
Ciò può complicare l'uso di operatori di confronto rigorosi:
const myStringLiteral = "My string";
const myStringObject = new String( "My string" );
myStringLiteral === "My string";
> true
myStringObject === "My string";
> false
Inserimento automatico del punto e virgola (ASI)
Durante l'analisi di uno script, gli interpreti di JavaScript possono utilizzare la funzionalità chiamata l'inserimento automatico del punto e virgola (ASI) per cercare di correggere le istanze di omissioni e un punto e virgola. Se l'analizzatore sintattico di JavaScript rileva un token non consentito, cerca di aggiungere un punto e virgola prima del token per correggere il potenziale errore di sintassi, purché una o più delle seguenti condizioni siano vere:
- Il token è separato dal token precedente da un'interruzione di riga.
- Il token è
}
. - Il token precedente è
)
e il punto e virgola inserito sarebbe la fine punto e virgola di un'istruzionedo
...while
.
Per ulteriori informazioni, consulta le regole ASI.
Ad esempio, l'omissione di punti e virgola dopo le seguenti istruzioni non causerà errore di sintassi dovuto ad ASI:
const myVariable = 2
myVariable + 3
> 5
Tuttavia, ASI non può tenere conto di più estratti conto sulla stessa riga. Se scrivere più istruzioni nella stessa riga, accertati di separarle con Punti e virgola:
const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier
const myVariable = 2; myVariable + 3;
> 5
L'ASI è un tentativo di correzione degli errori, non un tipo di flessibilità sintattica in JavaScript. Assicurati di utilizzare il punto e virgola dove appropriato, in modo da non fare affidamento per produrre il codice corretto.
Modalità più restrittiva
Gli standard che regolano la scrittura di JavaScript si sono evoluti ben oltre a qualsiasi aspetto preso in considerazione durante la fase iniziale di progettazione del linguaggio. Ogni nuova modifica alle Il comportamento previsto di JavaScript deve evitare di generare errori nei siti web meno recenti.
ES5 risolve alcuni problemi di lunga data relativi alla semantica di JavaScript senza
interrompere le implementazioni esistenti con l'introduzione
della "modalità rigida", un modo per scegliere
in un insieme più restrittivo di regole linguistiche per un intero script o
funzione individuale. Per abilitare la modalità con restrizioni, utilizza il valore letterale stringa
"use strict"
, seguito da un punto e virgola, sulla prima riga di uno script oppure
:
"use strict";
function myFunction() {
"use strict";
}
La modalità con restrizioni impedisce alcuni contenuti "non sicuri" azioni o funzionalità deprecate,
Errori espliciti al posto dei comuni elementi "silenziosi" e vieta l'uso di
che potrebbero entrare in conflitto con caratteristiche linguistiche future. Ad esempio, all'inizio
progettare le decisioni relative all'ambito variabile
ha aumentato le probabilità che gli sviluppatori "inquinassero" per errore l'ambito globale quando
dichiarare una variabile, indipendentemente dal contesto contenitore, omettendo il codice
var
parola chiave:
(function() {
mySloppyGlobal = true;
}());
mySloppyGlobal;
> true
I runtime JavaScript moderni non possono correggere questo comportamento senza il rischio di danneggiare qualsiasi sito web che vi si basa, per errore o deliberatamente. Il JavaScript moderno lo impedisce, consentendo agli sviluppatori di attivare per i nuovi lavori e attivando la modalità con restrizioni per impostazione predefinita solo nel contesto funzionalità del nuovo linguaggio in cui non interrompono le implementazioni precedenti:
(function() {
"use strict";
mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal
Devi scrivere "use strict"
come
stringa letterale.
Un valore letterale modello
(use strict
) non funzionerà. Devi anche includere "use strict"
prima di qualsiasi
di codice eseguibile nel contesto previsto. In caso contrario, l'interprete la ignora.
(function() {
"use strict";
let myVariable = "String.";
console.log( myVariable );
sloppyGlobal = true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal
(function() {
let myVariable = "String.";
"use strict";
console.log( myVariable );
sloppyGlobal = true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope
Per riferimento, per valore
Qualsiasi variabile, tra cui le proprietà di un oggetto, parametri di funzione ed elementi in una array, set o mappa, può contenere una primitiva o un valore di riferimento.
Quando un valore primitivo viene assegnato da una variabile a un'altra, il codice JavaScript crea una copia di quel valore e la assegna alla variabile.
Quando assegni un oggetto (istanze di classe, array e funzioni) a un , invece di creare una nuova copia dell'oggetto, contiene una riferimento alla posizione archiviata dell'oggetto in memoria. Per questo motivo, un oggetto a cui si fa riferimento da una variabile cambia l'oggetto a cui fa riferimento, non solo un valore contenuto in quella variabile. Ad esempio, se inizializzi un nuovo con una variabile contenente un riferimento a un oggetto, usa la nuova per aggiungere una proprietà all'oggetto, la proprietà e il relativo valore vengono aggiunti all'oggetto originale:
const myObject = {};
const myObjectReference = myObject;
myObjectReference.myProperty = true;
myObject;
> Object { myProperty: true }
Questo è importante non solo per alterare gli oggetti, ma anche per eseguire rigide
confronti, perché l'uguaglianza rigorosa tra gli oggetti richiede
fai riferimento allo stesso oggetto per restituire true
. Non possono fare riferimento
diversi, anche se sono strutturalmente identici:
const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};
myObject === myNewObject;
> false
myObject === myReferencedObject;
> true
Allocazione della memoria
JavaScript utilizza la gestione automatica della memoria, il che significa che la memoria non è necessaria esplicitamente allocati o distribuiti nel corso dello sviluppo. Mentre i dettagli del motore JavaScript alla gestione della memoria vanno oltre nell'ambito di questo modulo, comprendere come viene allocata la memoria contesto per l'uso dei valori di riferimento.
Esistono due "aree" in memoria: lo "stack" e "heap". Lo stack archivia dati statici (valori primitivi e riferimenti agli oggetti), perché di spazio fisso necessario per archiviare questi dati prima che dello script. L'heap archivia gli oggetti che richiedono uno spazio allocato dinamicamente perché possono cambiare durante l'esecuzione. La memoria viene liberata da un processo chiamata "garbage collection" che rimuove gli oggetti senza riferimenti la memoria.
Il thread principale
JavaScript è un linguaggio sostanzialmente a thread unico con una struttura "sincrona" di esecuzione, il che significa che può eseguire una sola attività alla volta. Questo contesto di esecuzione sequenziale è chiamato thread principale.
Il thread principale è condiviso da altre attività del browser, come l'analisi dell'HTML, il rendering di parti della pagina, l'esecuzione di animazioni CSS e gestire le interazioni degli utenti, da quelle semplici (come l'evidenziazione del testo) complessi (come l'interazione con gli elementi del modulo). I fornitori di browser hanno trovato per ottimizzare le attività eseguite dal thread principale, ma più complesse gli script possono comunque utilizzare troppe risorse del thread principale e influire sul complesso le prestazioni della pagina.
Alcune attività possono essere eseguite in in thread chiamati web worker, con alcune limitazioni:
- I thread worker possono agire solo su file JavaScript autonomi.
- L'accesso alla finestra e all'interfaccia utente del browser è stato notevolmente ridotto o nullo.
- Il modo in cui possono comunicare con il thread principale è limitato.
Questi limiti li rendono ideali per attività concentrate e ad alto impiego di risorse, altrimenti occupare il thread principale.
Stack di chiamate
La struttura dei dati utilizzata per gestire i "contesti di esecuzione", ovvero il codice eseguite attivamente: si tratta di un elenco chiamato stack di chiamate (spesso "lo stack"). Alla prima esecuzione di uno script, l'interprete JavaScript crea un "contesto di esecuzione globale" e ne esegue il push nello stack di chiamate, all'interno del contesto globale, eseguite una alla volta, dall'alto in basso. Quando l'interprete rileva una chiamata di funzione durante l'esecuzione del comando contesto globale, spinge un "contesto di esecuzione della funzione" per quella chiamata all'inizio dello stack, mette in pausa il contesto di esecuzione globale ed esegue la funzione contesto di esecuzione.
Ogni volta che viene chiamata una funzione, il contesto di esecuzione della funzione per quella chiamata viene all'inizio dello stack, appena sopra il contesto di esecuzione corrente. Lo stack di chiamate opera sulla base di un "first in, first out" il che significa che una chiamata di funzione recente, che è il più in alto nello stack, viene eseguita e continua fino alla risoluzione. Quando la funzione è completata, l'interprete la rimuove dallo stack di chiamate e dal contesto di esecuzione che contiene la chiamata di funzione diventa di nuovo l'elemento più in alto nello stack e riprende l'esecuzione.
Questi contesti di esecuzione acquisiscono tutti i valori necessari alla loro esecuzione. Loro
stabilire anche le variabili e le funzioni disponibili nell'ambito
funzione in base al contesto padre e determina e imposta il valore della
this
parola chiave nel contesto della funzione.
Il loop di eventi e la coda di callback
Questa esecuzione sequenziale significa che le attività asincrone che includono
come il recupero dei dati da un server, la risposta all'interazione dell'utente,
o in attesa di timer impostati con setTimeout
o setInterval
, bloccherebbe
nel thread principale finché l'attività non viene completata o interrompere inaspettatamente
contesto di esecuzione attuale nel momento in cui il contesto di esecuzione della funzione di callback
vengono aggiunte all'elenco filtri. Per risolvere questo problema, JavaScript gestisce le attività asincrone
utilizzando un "modello di contemporaneità" basato su eventi costituito dal "loop di eventi" e ai
"coda di callback" (chiamata a volte "coda di messaggi").
Quando un'attività asincrona viene eseguita sul thread principale, il callback il contesto di esecuzione della funzione si trova nella coda di callback, non in cima al e lo stack di chiamate. Il loop di eventi è un pattern talvolta chiamato reactor, che continua esegue il polling dello stato dello stack di chiamate e della coda di callback. Se ci sono attività in la coda di callback e il loop di eventi determinano che lo stack di chiamate è vuoto, dalla coda di callback vengono inviate allo stack una alla volta per eseguito.