Qual è la "nuova" parola chiave in JavaScript?


1744

La newparola chiave in JavaScript può essere piuttosto confusa quando viene incontrata per la prima volta, poiché le persone tendono a pensare che JavaScript non sia un linguaggio di programmazione orientato agli oggetti.

  • Che cos'è?
  • Quali problemi risolve?
  • Quando è appropriato e quando no?

10

Risposte:


2145

Fa 5 cose:

  1. Crea un nuovo oggetto. Il tipo di questo oggetto è semplicemente oggetto .
  2. Imposta la proprietà [, prototipo]] interna (inaccessibile) di questo nuovo oggetto (cioè __proto__ ) come oggetto prototipo esterno, accessibile, della funzione costruttore (ogni oggetto funzione ha automaticamente una proprietà prototipo ).
  3. Fa in modo che la thisvariabile punti all'oggetto appena creato.
  4. Esegue la funzione di costruzione, utilizzando l'oggetto appena creato ogni volta che thisviene menzionato.
  5. Restituisce l'oggetto appena creato, a meno che la funzione di costruzione non restituisca un nullriferimento non oggetto. In questo caso, viene invece restituito quel riferimento all'oggetto.

Nota: la funzione di costruzione si riferisce alla funzione dopo la newparola chiave, come in

new ConstructorFunction(arg1, arg2)

Una volta fatto questo, se viene richiesta una proprietà indefinita del nuovo oggetto, lo script controllerà invece l'oggetto [[prototype]] dell'oggetto per la proprietà. Ecco come ottenere qualcosa di simile all'eredità di classe tradizionale in JavaScript.

La parte più difficile di questo è il punto numero 2. Ogni oggetto (comprese le funzioni) ha questa proprietà interna chiamata [[prototipo]] . Può essere impostato solo al momento della creazione dell'oggetto, con new , con Object.create o in base al valore letterale (funzioni predefinite su Function.prototype, numeri su Number.prototype, ecc.). Può essere letto solo con Object.getPrototypeOf (someObject) . Non esiste altro modo per impostare o leggere questo valore.

Le funzioni, oltre alla proprietà nascosta [[prototype]] , hanno anche una proprietà chiamata prototype , ed è a questo che puoi accedere e modificare per fornire proprietà e metodi ereditati per gli oggetti che crei.


Ecco un esempio:

ObjMaker = function() {this.a = 'first';};
// ObjMaker is just a function, there's nothing special about it that makes 
// it a constructor.

ObjMaker.prototype.b = 'second';
// like all functions, ObjMaker has an accessible prototype property that 
// we can alter. I just added a property called 'b' to it. Like 
// all objects, ObjMaker also has an inaccessible [[prototype]] property
// that we can't do anything with

obj1 = new ObjMaker();
// 3 things just happened.
// A new, empty object was created called obj1.  At first obj1 was the same
// as {}. The [[prototype]] property of obj1 was then set to the current
// object value of the ObjMaker.prototype (if ObjMaker.prototype is later
// assigned a new object value, obj1's [[prototype]] will not change, but you
// can alter the properties of ObjMaker.prototype to add to both the
// prototype and [[prototype]]). The ObjMaker function was executed, with
// obj1 in place of this... so obj1.a was set to 'first'.

obj1.a;
// returns 'first'
obj1.b;
// obj1 doesn't have a property called 'b', so JavaScript checks 
// its [[prototype]]. Its [[prototype]] is the same as ObjMaker.prototype
// ObjMaker.prototype has a property called 'b' with value 'second'
// returns 'second'

È come l'ereditarietà di classe perché ora qualsiasi oggetto che fai usando new ObjMaker()sembrerà aver ereditato la proprietà 'b'.

Se vuoi qualcosa come una sottoclasse, allora fai questo:

SubObjMaker = function () {};
SubObjMaker.prototype = new ObjMaker(); // note: this pattern is deprecated!
// Because we used 'new', the [[prototype]] property of SubObjMaker.prototype
// is now set to the object value of ObjMaker.prototype.
// The modern way to do this is with Object.create(), which was added in ECMAScript 5:
// SubObjMaker.prototype = Object.create(ObjMaker.prototype);

SubObjMaker.prototype.c = 'third';  
obj2 = new SubObjMaker();
// [[prototype]] property of obj2 is now set to SubObjMaker.prototype
// Remember that the [[prototype]] property of SubObjMaker.prototype
// is ObjMaker.prototype. So now obj2 has a prototype chain!
// obj2 ---> SubObjMaker.prototype ---> ObjMaker.prototype

obj2.c;
// returns 'third', from SubObjMaker.prototype

obj2.b;
// returns 'second', from ObjMaker.prototype

obj2.a;
// returns 'first', from SubObjMaker.prototype, because SubObjMaker.prototype 
// was created with the ObjMaker function, which assigned a for us

Ho letto un sacco di spazzatura su questo argomento prima di trovare finalmente questa pagina , dove questo è spiegato molto bene con bei diagrammi.


47
Volevo solo aggiungere: esiste infatti un modo per accedere al [[prototipo]] interno, di __proto__. Questo è tuttavia non standard e supportato solo da browser relativamente nuovi (e non tutti). Esiste un modo standardizzato in arrivo, vale a dire Object.getPrototypeOf (obj), ma è Ecmascript3.1, ed è esso stesso supportato solo su nuovi browser - di nuovo. In genere si consiglia di non utilizzare quella proprietà, tuttavia le cose si complicano molto velocemente all'interno.
Blub,

9
Domanda: cosa succede diversamente se ObjMakerè definita come una funzione che restituisce un valore?
Jim Blackler,

13
@LonelyPixel newesiste in modo da non dover scrivere metodi di fabbrica per costruire / copiare funzioni / oggetti. Significa "Copia questo, rendendolo proprio come la sua 'classe' genitore; fallo in modo efficiente e corretto; e memorizza le informazioni sull'eredità che sono accessibili solo a me, JS, internamente". Per fare ciò, modifica l'interno altrimenti inaccessibile prototypedel nuovo oggetto per incapsulare opzionalmente i membri ereditati, imitando le catene di ereditarietà OO classiche (che non sono modificabili in fase di runtime). Puoi simularlo senza new, ma l'ereditarietà sarà modificabile in fase di esecuzione. Buona? Cattivo? Sta a te.
Ingegnere

11
un piccolo punto da aggiungere: una chiamata a un costruttore, quando preceduta dalla nuova parola chiave, restituisce automaticamente l'oggetto creato; non è necessario restituirlo esplicitamente all'interno del costruttore.
charlie roberts,

7
C'è una nota che dice Notice that this pattern is deprecated!. Qual è il modello aggiornato corretto per impostare il prototipo di una classe?
Tom Pažourek,

400

Supponiamo di avere questa funzione:

var Foo = function(){
  this.A = 1;
  this.B = 2;
};

Se la chiamate come una funzione autonoma in questo modo:

Foo();

L'esecuzione di questa funzione aggiungerà due proprietà windowall'oggetto ( Ae B). Lo aggiunge al windowperché windowè l'oggetto che ha chiamato la funzione quando la si esegue in quel modo, e thisin una funzione è l'oggetto che ha chiamato la funzione. Almeno in Javascript.

Ora chiamalo così con new:

var bar = new Foo();

Ciò che accade quando si aggiunge newa una chiamata di funzione è che un nuovo oggetto viene creato (solo var bar = new Object()) e che thisall'interno della funzione punta al nuovo Objectappena creato, anziché all'oggetto che ha chiamato la funzione. Quindi barora è un oggetto con le proprietà Ae B. Qualsiasi funzione può essere un costruttore, semplicemente non ha sempre senso.


7
Dipende dal contesto di esecuzione. Nel mio caso (Qt scripting) è solo un oggetto globale.
Maxym,

2
questo causerà un maggiore utilizzo della memoria?
Jürgen Paul,

2
perché window è l'oggetto che ha chiamato la funzione - deve essere: perché window è l'oggetto che contiene la funzione.
Dávid Horváth,

1
@Taurus In un browser Web una funzione non metodo sarà windowimplicitamente un metodo . Anche in una chiusura, anche se anonimato. Tuttavia, nell'esempio si tratta di un semplice metodo di richiamo sulla finestra: Foo();=> [default context].Foo();=> window.Foo();. In questa espressione windowè il contesto (non solo il chiamante , che non ha importanza).
Dávid Horváth,

1
@Taurus Fondamentalmente sì. Tuttavia, in ECMA 6 e 7 le cose sono più complesse (vedi lambda, classi, ecc.).
Dávid Horváth,

164

Oltre alla risposta di Daniel Howard, ecco cosa newfa (o almeno sembra fare):

function New(func) {
    var res = {};
    if (func.prototype !== null) {
        res.__proto__ = func.prototype;
    }
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return res;
}

Mentre

var obj = New(A, 1, 2);

è equivalente a

var obj = new A(1, 2);

73
Ho scoperto che javascript è più facile da capire
dell'inglese

Risposta eccellente. Ho una piccola domanda: come può essere possibile func.prototypeessere null? Potresti per favore approfondire un po 'quello?
Tom Pažourek,

6
@tomp potresti sovrascrivere la proprietà del prototipo, semplicemente scrivendo A.prototype = null;In tal caso si new A()otterrà l'oggetto, il prototipo interno punta Objectall'oggetto: jsfiddle.net/Mk42Z
basilikum

2
Il controllo del tipo potrebbe essere errato perché un oggetto host potrebbe produrre qualcosa di diverso da "oggetto" o "funzione". Per verificare se qualcosa è un oggetto, preferisco Object(ret) === ret.
Oriol,

2
@Oriol grazie per il commento. È vero quello che dici e qualsiasi test effettivo dovrebbe essere fatto in modo più robusto. Tuttavia, penso che per questa risposta concettuale, il typeoftest semplifichi la comprensione di ciò che sta accadendo dietro le quinte.
basilikum,

109

Per i principianti di capirlo meglio

prova il seguente codice nella console del browser.

function Foo() { 
    return this; 
}

var a = Foo();       //returns window object
var b = new Foo();   //returns empty object of foo

a instanceof Window;  // true
a instanceof Foo;     // false

b instanceof Window;  // false
b instanceof Foo;     // true

Ora puoi leggere la risposta della wiki della community :)


4
Buona risposta. Inoltre, tralasciando si return this;ottiene lo stesso output.
Nelu,

37

quindi probabilmente non è per la creazione di istanze di oggetti

È usato esattamente per quello. Definisci un costruttore di funzioni in questo modo:

function Person(name) {
    this.name = name;
}

var john = new Person('John');

Tuttavia, l'ulteriore vantaggio che ECMAScript ha è che puoi estenderlo con la .prototypeproprietà, quindi possiamo fare qualcosa come ...

Person.prototype.getName = function() { return this.name; }

Tutti gli oggetti creati da questo costruttore ora avranno una getNamecausa della catena di prototipi a cui hanno accesso.


6
i costruttori di funzioni sono usati come le classi, non esiste una classparola chiave ma puoi praticamente fare la stessa cosa.
meder omuraliev,

Esiste una parola chiave class - la classe è riservata per uso futuro
Greg,

11
Per inciso, è per questo che usi .className non .class per impostare una classe CSS
Greg,

23
Dovrebbe essere Persona maiuscola per convenzione.
eomeroff,

27

JavaScript è un linguaggio di programmazione orientato agli oggetti ed è utilizzato esattamente per la creazione di istanze. È basato sul prototipo, piuttosto che sulla classe, ma ciò non significa che non sia orientato agli oggetti.


6
Mi piace dire che JavaScript sembra essere ancora più orientato agli oggetti rispetto a tutti quei linguaggi di classe. In JavaScript tutto ciò che scrivi diventa immediatamente un oggetto, ma in linguaggi basati su classi scrivi prima le dichiarazioni e solo successivamente crei istanze specifiche (oggetti) di classi. E il prototipo JavaScript sembra ricordare vagamente tutto ciò che VTABLE per i linguaggi di classe.
JustAMartin,

14

Javascript è un linguaggio di programmazione dinamico che supporta il paradigma di programmazione orientato agli oggetti e viene utilizzato per creare nuove istanze di oggetti.

Le classi non sono necessarie per gli oggetti - Javascript è un linguaggio basato su prototipo .


12

Ci sono già delle ottime risposte, ma ne sto pubblicando una nuova per enfatizzare la mia osservazione sul caso III di seguito su ciò che accade quando si ha una dichiarazione di ritorno esplicita in una funzione che si sta newelaborando. Dai un'occhiata ai casi seguenti:

Caso I :

var Foo = function(){
  this.A = 1; 
  this.B = 2;
};
console.log(Foo()); //prints undefined
console.log(window.A); //prints 1

Sopra è un semplice caso di chiamare la funzione anonima indicata da Foo. Quando chiamate questa funzione, ritorna undefined. Poiché non esiste un'istruzione di ritorno esplicita, l'interprete JavaScript inserisce forzatamente return undefined;un'istruzione alla fine della funzione. Qui la finestra è l'oggetto di invocazione (contestuale this) che ottiene nuovi Ae Bproprietà.

Caso II :

var Foo = function(){
  this.A = 1;
  this.B = 2;
};
var bar = new Foo();
console.log(bar()); //illegal isn't pointing to a function but an object
console.log(bar.A); //prints 1

Qui l'interprete JavaScript che vede la newparola chiave crea un nuovo oggetto che funge da oggetto di invocazione (contestuale this) della funzione anonima indicata da Foo. In questo caso Ae Bdiventare proprietà dell'oggetto appena creato (al posto dell'oggetto finestra). Poiché non si dispone di alcuna dichiarazione di ritorno esplicita, l'interprete JavaScript inserisce forzatamente una dichiarazione di ritorno per restituire il nuovo oggetto creato a causa dell'uso della newparola chiave.

Caso III :

var Foo = function(){
  this.A = 1;
  this.B = 2;
  return {C:20,D:30}; 
};
var bar = new Foo();
console.log(bar.C);//prints 20
console.log(bar.A); //prints undefined. bar is not pointing to the object which got created due to new keyword.

Anche in questo caso l'interprete JavaScript che vede la newparola chiave crea un nuovo oggetto che funge da oggetto di invocazione (contestuale this) della funzione anonima indicata da Foo. Ancora una volta, Ae Bdiventa proprietà sull'oggetto appena creato. Ma questa volta hai una dichiarazione di ritorno esplicita, quindi l'interprete JavaScript non farà nulla di proprio.

La cosa da notare nel caso III è che l'oggetto creato a causa della newparola chiave si è perso dal radar. barindica in realtà un oggetto completamente diverso che non è quello creato dall'interprete JavaScript a causa della newparola chiave.

Citando David Flanagan da JavaScripit: The Definitive Guide (6th Edition), cap. 4, Pagina # 62:

Quando viene valutata un'espressione di creazione di oggetti, JavaScript crea innanzitutto un nuovo oggetto vuoto, proprio come quello creato dall'inizializzatore di oggetti {}. Successivamente, invoca la funzione specificata con gli argomenti specificati, passando il nuovo oggetto come valore di questa parola chiave. La funzione può quindi utilizzarlo per inizializzare le proprietà dell'oggetto appena creato. Le funzioni scritte per essere utilizzate come costruttori non restituiscono un valore e il valore dell'espressione di creazione dell'oggetto è l'oggetto appena creato e inizializzato. Se un costruttore restituisce un valore oggetto, quel valore diventa il valore dell'espressione di creazione dell'oggetto e l'oggetto appena creato viene scartato.

--- Ulteriori informazioni ---

Le funzioni utilizzate nello snippet di codice dei casi precedenti hanno nomi speciali nel mondo JS come di seguito:

Caso I e II - Funzione di costruzione

Caso III - Funzione di fabbrica. Le funzioni di fabbrica non dovrebbero essere usate con la newparola chiave che ho fatto per spiegare il concetto nel thread corrente.

Puoi leggere le differenze tra loro in questo thread.


il tuo caso 3, è un'osservazione gr8
appu

5

a volte il codice è più facile delle parole:

var func1 = function (x) { this.x = x; }                    // used with 'new' only
var func2 = function (x) { var z={}; z.x = x; return z; }   // used both ways
func1.prototype.y = 11;
func2.prototype.y = 12;

A1 = new func1(1);      // has A1.x  AND  A1.y
A2 =     func1(1);      // undefined ('this' refers to 'window')
B1 = new func2(2);      // has B1.x  ONLY
B2 =     func2(2);      // has B2.x  ONLY

per me, fintanto che non prototipo, utilizzo lo stile di func2 in quanto mi dà un po 'più di flessibilità all'interno e all'esterno della funzione.


3
B1 = new func2(2); <- Perché questo non avrà B1.y ?
sunny_dev,

@sunny_dev Non sono un esperto di JS, ma probabilmente perché func2 sta restituendo direttamente un valore (oggetto z), invece di lavorare / restituire con valori interni (questo)
Eagle,

5

La newparola chiave cambia il contesto in cui viene eseguita la funzione e restituisce un puntatore a quel contesto.

Quando non si utilizza la newparola chiave, il contesto in cui Vehicle()viene eseguita la funzione è lo stesso contesto da cui si sta chiamando la Vehiclefunzione. La thisparola chiave farà riferimento allo stesso contesto. Quando si utilizza new Vehicle(), viene creato un nuovo contesto in modo che la parola chiave thisall'interno della funzione si riferisca al nuovo contesto. Ciò che ottieni in cambio è il contesto appena creato.


Questa è una risposta molto approfondita in termini di portata. Gr8 oltre alla risposta.
appu

3

La newparola chiave è per creare nuove istanze di oggetti. E sì, JavaScript è un linguaggio di programmazione dinamico, che supporta il paradigma di programmazione orientato agli oggetti. La convenzione sulla denominazione degli oggetti è, usa sempre la lettera maiuscola per gli oggetti che dovrebbero essere istanziati dalla nuova parola chiave.

obj = new Element();

2

Sommario:

La newparola chiave viene utilizzata in JavaScript per creare un oggetto da una funzione di costruzione. La newparola chiave deve essere posizionata prima della chiamata della funzione di costruzione e farà le seguenti cose:

  1. Crea un nuovo oggetto
  2. Imposta il prototipo di questo oggetto sulla proprietà prototype della funzione di costruzione
  3. Associa la thisparola chiave all'oggetto appena creato ed esegue la funzione di costruzione
  4. Restituisce l'oggetto appena creato

Esempio:

function Dog (age) {
  this.age = age;
}

const doggie = new Dog(12);

console.log(doggie);
console.log(doggie.__proto__ === Dog.prototype) // true

Cosa succede esattamente:

  1. const doggie dice: Abbiamo bisogno di memoria per dichiarare una variabile.
  2. L'operatore assigment =dice: Inizializzeremo questa variabile con l'espressione dopo il=
  3. L'espressione è new Dog(12). Il motore JS vede la nuova parola chiave, crea un nuovo oggetto e imposta il prototipo su Dog.prototype
  4. La funzione di costruzione viene eseguita con il thisvalore impostato sul nuovo oggetto. In questo passaggio è dove l'età è assegnata al nuovo oggetto cagnolino creato.
  5. L'oggetto appena creato viene restituito e assegnato al cagnolino variabile.

1

La newparola chiave crea istanze di oggetti usando le funzioni come costruttore. Per esempio:

var Foo = function() {};
Foo.prototype.bar = 'bar';

var foo = new Foo();
foo instanceof Foo; // true

Istanze ereditate dalla prototypefunzione del costruttore. Quindi, dato l'esempio sopra ...

foo.bar; // 'bar'

2
La nuova parola chiave fondamentalmente associa già la funzione come costruttore; non è necessario restituire nulla. Puoi semplicemente fare: funzione pippo (x) {this.bar = x; } var obj = new foo (10); alert (obj.bar);
reko_t,

Non è necessario restituire oggetti dalla funzione di costruzione a meno che non si desideri specificatamente, per uno scopo. Ad esempio, se è necessario restituire un'istanza di oggetto specifica invece di creare un nuovo oggetto ogni volta (per qualsiasi motivo). Nel tuo esempio, tuttavia, è totalmente inutile.
Chetan Sastry,

Bene, è stato un esempio. È possibile restituire un oggetto. Ci sono molti schemi usati in questo scenario, ne ho fornito uno come "per esempio", quindi le mie parole "per esempio".
mancanza di palpebre

1

Bene JavaScript per si può differire notevolmente da una piattaforma all'altra in quanto è sempre un'implementazione della specifica originale EcmaScript.

In ogni caso, indipendentemente dall'implementazione tutte le implementazioni JavaScript che seguono le specifiche EcmaScript, ti daranno un linguaggio orientato agli oggetti. Secondo lo standard ES:

ECMAScript è un linguaggio di programmazione orientato agli oggetti per eseguire calcoli e manipolare oggetti computazionali all'interno di un ambiente host.

Quindi, ora che abbiamo concordato che JavaScript è un'implementazione di EcmaScript e quindi è un linguaggio orientato agli oggetti. La definizione newdell'operazione in qualsiasi linguaggio orientato agli oggetti indica che tale parola chiave viene utilizzata per creare un'istanza di oggetto da una classe di un certo tipo (compresi i tipi anonimi, in casi come C #).

In EcmaScript non utilizziamo le classi, come puoi leggere dalle specifiche:

ECMAScript non utilizza classi come quelle in C ++, Smalltalk o Java. Invece gli oggetti possono essere creati in vari modi, anche tramite una notazione letterale o tramite costruttori che creano oggetti e quindi eseguono codice che li inizializza in tutto o in parte assegnando i valori iniziali alle loro proprietà. Ogni costruttore è una funzione che ha una proprietà denominata - prototype ‖ che viene utilizzata per implementare l'ereditarietà basata sul prototipo e le proprietà condivise. Gli oggetti vengono creati
usando i costruttori in nuove espressioni; ad esempio, new Date (2009,11) crea un nuovo oggetto Date. Richiamare un costruttore senza usare il nuovo ha conseguenze che dipendono dal costruttore. Ad esempio, Date () produce una rappresentazione in formato stringa della data e ora correnti anziché un oggetto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.