Qu'est-ce que le «nouveau» mot clé en JavaScript?

1745

Le newmot clé en JavaScript peut être assez déroutant lors de sa première rencontre, car les gens ont tendance à penser que JavaScript n'est pas un langage de programmation orienté objet.

  • Qu'Est-ce que c'est?
  • Quels problèmes cela résout-il?
  • Quand est-ce approprié et quand non?
Alon Gubkin
la source
10
Aussi, fil connexe - stackoverflow.com/questions/383402/…
Chetan Sastry
lisez d'abord ces exemples, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Martian2049

Réponses:

2145

Il fait 5 choses:

  1. Il crée un nouvel objet. Le type de cet objet est simplement objet .
  2. Il définit cette nouvelle, interne de l' objet, inaccessible [[prototype]] (c. -à- __proto__ ) propriété d'être l'extérieur de la fonction constructeur, accessible, prototype objet (chaque objet de fonction a automatiquement un prototype propriété).
  3. Il fait thispointer la variable vers l'objet nouvellement créé.
  4. Il exécute la fonction constructeur, en utilisant l'objet nouvellement créé chaque fois qu'il thisest mentionné.
  5. Elle renvoie l'objet nouvellement créé, sauf si la fonction constructeur renvoie une nullréférence non- objet. Dans ce cas, cette référence d'objet est renvoyée à la place.

Remarque: la fonction constructeur fait référence à la fonction après le newmot - clé, comme dans

new ConstructorFunction(arg1, arg2)

Une fois cela fait, si une propriété non définie du nouvel objet est demandée, le script vérifiera plutôt l'objet [[prototype]] de l' objet pour la propriété. C'est ainsi que vous pouvez obtenir quelque chose de similaire à l'héritage de classe traditionnel en JavaScript.

La partie la plus difficile à ce sujet est le point numéro 2. Chaque objet (y compris les fonctions) possède cette propriété interne appelée [[prototype]] . Il ne peut être défini qu'au moment de la création de l'objet, soit avec new , avec Object.create , soit en fonction du littéral (fonctions par défaut à Function.prototype, nombres à Number.prototype, etc.). Il ne peut être lu qu'avec Object.getPrototypeOf (someObject) . Il n'existe aucun autre moyen de définir ou de lire cette valeur.

Les fonctions, en plus de la propriété cachée [[prototype]] , ont également une propriété appelée prototype , et c'est à cela que vous pouvez accéder et la modifier pour fournir des propriétés et des méthodes héritées aux objets que vous créez.


Voici un exemple:

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'

C'est comme l'héritage de classe car maintenant, tous les objets que vous créez en utilisant new ObjMaker()sembleront également avoir hérité de la propriété «b».

Si vous voulez quelque chose comme une sous-classe, alors vous faites ceci:

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

J'ai lu une tonne d'ordures sur ce sujet avant de retrouver enfin cette page , où cela s'explique très bien avec de jolis diagrammes.

Daniel Howard
la source
47
Je voulais juste ajouter: il y a en fait un moyen d'accéder au [[prototype]] interne, par __proto__. Ceci n'est cependant pas standard et n'est pris en charge que par des navigateurs relativement nouveaux (et pas tous). Il existe un moyen standardisé à venir, à savoir Object.getPrototypeOf (obj), mais c'est Ecmascript3.1, et n'est lui-même pris en charge que sur les nouveaux navigateurs - encore une fois. Il est généralement recommandé de ne pas utiliser cette propriété, les choses se compliquent très vite à l'intérieur.
Blub
9
Question: que se passe-t-il différemment si ObjMakerest définie comme une fonction qui renvoie une valeur?
Jim Blackler
13
@LonelyPixel newexiste afin que vous n'ayez pas à écrire de méthodes d'usine pour construire / copier des fonctions / objets. Cela signifie, "Copiez ceci, en le faisant exactement comme sa 'classe' parent; faites-le efficacement et correctement; et stockez les informations d'héritage qui ne sont accessibles qu'à moi, JS, en interne". Pour ce faire, il modifie l'interne autrement inaccessible prototypedu nouvel objet pour encapsuler de manière opaque les membres hérités, imitant les chaînes d'héritage OO classiques (qui ne sont pas modifiables au moment de l'exécution). Vous pouvez simuler cela sans new, mais l'héritage sera modifiable à l'exécution. Bien? Mauvais? Dépend de vous.
Ingénieur
11
un petit point à ajouter: un appel à un constructeur, lorsqu'il est précédé du nouveau mot-clé, renvoie automatiquement l'objet créé; il n'est pas nécessaire de le renvoyer explicitement depuis le constructeur.
charlie roberts
7
Il y a une note qui dit Notice that this pattern is deprecated!. Quel est le modèle de mise à jour correct pour définir le prototype d'une classe?
Tom Pažourek
400

Supposons que vous ayez cette fonction:

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

Si vous appelez cela comme une fonction autonome comme ceci:

Foo();

L'exécution de cette fonction ajoutera deux propriétés à l' windowobjet ( Aet B). Il l'ajoute au windowcar windowest l'objet qui a appelé la fonction lorsque vous l'exécutez comme ça, et thisdans une fonction se trouve l'objet qui a appelé la fonction. En Javascript au moins.

Maintenant, appelez-le comme ceci avec new:

var bar = new Foo();

Ce qui se passe lorsque vous ajoutez newà un appel de fonction, c'est qu'un nouvel objet est créé (juste var bar = new Object()) et que l' thisintérieur de la fonction pointe vers le nouveau que Objectvous venez de créer, au lieu de l'objet qui a appelé la fonction. Il en barest de même d'un objet avec les propriétés Aet B. Toute fonction peut être un constructeur, cela n'a tout simplement pas toujours de sens.

JulianR
la source
7
Dépend du contexte d'exécution. Dans mon cas (script Qt), c'est juste un objet global.
Maxym
2
cela entraînera-t-il une utilisation accrue de la mémoire?
Jürgen Paul
2
parce que la fenêtre est l'objet qui a appelé la fonction - doit être: parce que la fenêtre est l'objet qui contient la fonction.
Dávid Horváth
1
@Taurus Dans un navigateur Web, une fonction non-méthode sera windowimplicitement une méthode . Même dans une fermeture, même si anonyme. Cependant, dans l'exemple, il s'agit d'une simple invocation de méthode sur la fenêtre: Foo();=> [default context].Foo();=> window.Foo();. Dans cette expression windowest le contexte (pas seulement l' appelant , ce qui n'a pas d'importance).
Dávid Horváth
1
@Taurus Fondamentalement, oui. Cependant, dans ECMA 6 et 7, les choses sont plus complexes (voir lambdas, classes, etc.).
Dávid Horváth
164

En plus de la réponse de Daniel Howard, voici ce que newfait (ou du moins semble faire):

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;
}

Tandis que

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

est équivalent à

var obj = new A(1, 2);
basilikum
la source
73
J'ai trouvé que javascript est plus facile à comprendre que l'anglais: v
wethat
Excellente réponse. J'ai une toute petite question: comment peut-il être possible func.prototypede l'être null? Pourriez-vous nous en dire un peu plus à ce sujet?
Tom Pažourek
6
@tomp vous pouvez remplacer la propriété du prototype, en écrivant simplement A.prototype = null;Dans ce cas, cela new A()se traduira par un objet, c'est le prototype interne qui pointe vers l' Objectobjet: jsfiddle.net/Mk42Z
basilikum
2
La vérification de typeof peut être erronée car un objet hôte peut produire quelque chose de différent de "objet" ou "fonction". Pour tester si quelque chose est un objet, je préfère Object(ret) === ret.
Oriol
2
@Oriol merci pour le commentaire. C'est vrai ce que vous dites et tout test réel devrait être fait de manière plus robuste. Cependant, je pense que pour cette réponse conceptuelle, le typeoftest permet simplement de comprendre plus facilement ce qui se passe dans les coulisses.
basilikum
109

Pour que les débutants le comprennent mieux

essayez le code suivant dans la console du navigateur.

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

Vous pouvez maintenant lire la réponse du wiki de la communauté :)

Anulal S
la source
4
Bonne réponse. De plus, laisser de côté return this;donne le même résultat.
Nelu
37

donc ce n'est probablement pas pour créer des instances d'objet

Il est utilisé exactement pour cela. Vous définissez un constructeur de fonction comme ceci:

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

var john = new Person('John');

Cependant, l'avantage supplémentaire d'ECMAScript est que vous pouvez l'étendre avec la .prototypepropriété, afin que nous puissions faire quelque chose comme ...

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

Tous les objets créés à partir de ce constructeur auront désormais un en getNameraison de la chaîne de prototype à laquelle ils ont accès.

meder omuraliev
la source
6
les constructeurs de fonctions sont utilisés comme des classes, il n'y a pas de classmot-clé mais vous pouvez à peu près faire la même chose.
meder omuraliev
Il y a en quelque sorte un mot-clé de classe - la classe est réservée pour une utilisation future
Greg
11
Soit dit en passant, c'est pourquoi vous utilisez .className et non .class pour définir une classe CSS
Greg
23
Il doit être capitalisé Personne par convention.
eomeroff
27

JavaScript est un langage de programmation orienté objet et il est utilisé exactement pour créer des instances. Il est basé sur un prototype plutôt que sur une classe, mais cela ne signifie pas qu'il n'est pas orienté objet.

Michael
la source
6
J'aime dire que JavaScript semble être encore plus orienté objet que tous ces langages basés sur les classes. En JavaScript, tout ce que vous écrivez devient immédiatement un objet, mais dans les langages basés sur les classes, vous écrivez d'abord des déclarations et ensuite vous créez des instances (objets) spécifiques de classes. Et le prototype JavaScript semble rappeler vaguement tout ce truc VTABLE pour les langages basés sur les classes.
JustAMartin
14

Javascript est un langage de programmation dynamique qui prend en charge le paradigme de programmation orienté objet, et il utilise utilisé pour créer de nouvelles instances d'objet.

Les classes ne sont pas nécessaires pour les objets - Javascript est un langage basé sur un prototype .

Greg
la source
12

Il existe déjà de très bonnes réponses, mais j'en publie une nouvelle pour souligner mon observation sur le cas III ci-dessous sur ce qui se passe lorsque vous avez une déclaration de retour explicite dans une fonction que vous êtes en newtrain de créer. Jetez un œil aux cas ci-dessous:

Cas I :

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

Ci-dessus est un cas simple d'appel de la fonction anonyme pointée par Foo. Lorsque vous appelez cette fonction, elle revient undefined. Puisqu'il n'y a pas d'instruction de retour explicite, l'interpréteur JavaScript insère avec force une return undefined;instruction à la fin de la fonction. Ici , la fenêtre est l'objet d'invocation (contextuelle this) qui obtient de nouveaux Aet Bpropriétés.

Cas 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

Ici, l'interpréteur JavaScript voyant le newmot - clé crée un nouvel objet qui agit comme l'objet d'invocation (contextuel this) de la fonction anonyme pointée par Foo. Dans ce cas Aet Bdevenez propriétés sur l'objet nouvellement créé (à la place de l'objet fenêtre). Étant donné que vous n'avez aucune instruction de retour explicite, l'interpréteur JavaScript insère avec force une instruction de retour pour renvoyer le nouvel objet créé en raison de l'utilisation du newmot - clé.

Cas 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.

Ici encore, l'interpréteur JavaScript voyant le newmot - clé crée un nouvel objet qui agit comme l'objet d'invocation (contextuel this) de la fonction anonyme pointée par Foo. Encore une fois, Aet Bdevenez des propriétés sur l'objet nouvellement créé. Mais cette fois, vous avez une déclaration de retour explicite, donc l'interpréteur JavaScript ne fera rien de lui-même.

La chose à noter dans le cas III est que l'objet créé en raison d'un newmot clé s'est perdu de votre radar. barpointe en fait vers un objet complètement différent qui n'est pas celui créé par l'interpréteur JavaScript à cause du newmot-clé.

Citant David Flanagan de JavaScripit: The Definitive Guide (6th Edition), Ch. 4, page n ° 62:

Lorsqu'une expression de création d'objet est évaluée, JavaScript crée d'abord un nouvel objet vide, tout comme celui créé par l'initialiseur d'objet {}. Ensuite, il appelle la fonction spécifiée avec les arguments spécifiés, en passant le nouvel objet comme valeur du mot-clé this. La fonction peut ensuite l'utiliser pour initialiser les propriétés de l'objet nouvellement créé. Les fonctions écrites pour être utilisées en tant que constructeurs ne renvoient pas de valeur, et la valeur de l'expression de création d'objet est l'objet nouvellement créé et initialisé. Si un constructeur renvoie une valeur d'objet, cette valeur devient la valeur de l'expression de création d'objet et le nouvel objet créé est ignoré.

--- Informations supplémentaires ---

Les fonctions utilisées dans l'extrait de code des cas ci-dessus ont des noms spéciaux dans le monde JS comme ci-dessous:

Cas I et II - Fonction constructeur

Cas III - Fonction d'usine. Les fonctions d'usine ne doivent pas être utilisées avec le newmot-clé que j'ai fait pour expliquer le concept dans le thread actuel.

Vous pouvez lire sur la différence entre eux dans ce fil.

RBT
la source
votre cas 3, est une observation gr8
appu
5

parfois le code est plus facile que les mots:

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

pour moi, tant que je ne fais pas de prototype, j'utilise le style de func2 car cela me donne un peu plus de flexibilité à l'intérieur et à l'extérieur de la fonction.

rsbkk
la source
3
B1 = new func2(2); <- Pourquoi cela n'aura pas B1.y ?
sunny_dev
@sunny_dev Je ne suis pas un expert JS, mais probablement parce que func2 retourne directement une valeur (objet z), au lieu de travailler / retourner avec des valeurs internes (ceci)
Eagle
5

Le newmot-clé modifie le contexte dans lequel la fonction est exécutée et renvoie un pointeur vers ce contexte.

Lorsque vous n'utilisez pas le newmot clé, le contexte sous lequel la fonction Vehicle()s'exécute est le même contexte à partir duquel vous appelez la Vehiclefonction. Le thismot-clé fera référence au même contexte. Lorsque vous utilisez new Vehicle(), un nouveau contexte est créé afin que le mot-clé thisà l'intérieur de la fonction fasse référence au nouveau contexte. Ce que vous obtenez en retour, c'est le contexte nouvellement créé.

Juzer Ali
la source
C'est une réponse très perspicace en termes de portée. Gr8 ajout à la réponse.
appu
3

Le newmot-clé sert à créer de nouvelles instances d'objets. Et oui, javascript est un langage de programmation dynamique, qui prend en charge le paradigme de programmation orienté objet. La convention concernant la dénomination des objets est de toujours utiliser des majuscules pour les objets censés être instanciés par le nouveau mot clé.

obj = new Element();
erenon
la source
2

Sommaire:

Le newmot-clé est utilisé en javascript pour créer un objet à partir d'une fonction constructeur. Le newmot-clé doit être placé avant l'appel de la fonction constructeur et fera les choses suivantes:

  1. Crée un nouvel objet
  2. Définit le prototype de cet objet sur la propriété prototype de la fonction constructeur
  3. Lie le thismot - clé à l'objet nouvellement créé et exécute la fonction constructeur
  4. Renvoie l'objet nouvellement créé

Exemple:

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

const doggie = new Dog(12);

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

Que se passe-t-il exactement:

  1. const doggie dit: Nous avons besoin de mémoire pour déclarer une variable.
  2. L'opérateur d'assignation =dit: Nous allons initialiser cette variable avec l'expression après le=
  3. L'expression est new Dog(12). Le moteur JS voit le nouveau mot-clé, crée un nouvel objet et définit le prototype sur Dog.prototype
  4. La fonction constructeur est exécutée avec la thisvaleur définie sur le nouvel objet. C'est à cette étape que l'âge est attribué au nouvel objet toutou créé.
  5. L'objet nouvellement créé est renvoyé et affecté au chien variable.
Willem van der Veen
la source
1

Le newmot-clé crée des instances d'objets en utilisant des fonctions comme constructeur. Par exemple:

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

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

Les instances héritent de la prototypefonction constructeur. Donc, étant donné l'exemple ci-dessus ...

foo.bar; // 'bar'
sans paupières
la source
2
Le nouveau mot-clé associe déjà la fonction en tant que constructeur; vous n'avez rien à retourner. Vous pouvez simplement faire: function foo (x) {this.bar = x; } var obj = new foo (10); alerte (obj.bar);
reko_t
Vous n'avez pas besoin de renvoyer des objets de la fonction constructeur sauf si vous le souhaitez spécifiquement, dans un but précis. Par exemple, si vous devez renvoyer une instance d'objet spécifique au lieu de créer un nouvel objet à chaque fois (pour une raison quelconque). Dans votre exemple, cependant, c'est totalement inutile.
Chetan Sastry
Eh bien, c'était un exemple. Vous pouvez renvoyer un objet. Il y a beaucoup de modèles utilisés dans ce scénario, j'en ai fourni un comme "par exemple", d'où mes mots "par exemple".
paupières
1

Eh bien, JavaScript per si peut différer considérablement d'une plate-forme à l'autre, car il s'agit toujours d'une implémentation de la spécification originale EcmaScript.

Dans tous les cas, indépendamment de l'implémentation, toutes les implémentations JavaScript qui suivent le droit de spécification EcmaScript, vous donneront un langage orienté objet. Selon la norme ES:

ECMAScript est un langage de programmation orienté objet pour effectuer des calculs et manipuler des objets de calcul dans un environnement hôte.

Alors maintenant que nous avons convenu que JavaScript est une implémentation d'EcmaScript et donc c'est un langage orienté objet. La définition de l' newopération dans n'importe quel langage orienté objet, dit qu'un tel mot clé est utilisé pour créer une instance d'objet à partir d'une classe d'un certain type (y compris les types anonymes, dans des cas comme C #).

En EcmaScript, nous n'utilisons pas de classes, comme vous pouvez le lire dans les spécifications:

ECMAScript n'utilise pas de classes telles que celles de C ++, Smalltalk ou Java. Au lieu de cela, les objets peuvent être créés de diverses manières, notamment via une notation littérale ou via des constructeurs qui créent des objets, puis exécutent du code qui les initialise en tout ou partie en attribuant des valeurs initiales à leurs propriétés. Chaque constructeur est une fonction qui possède une propriété nommée - prototype ‖ qui est utilisée pour implémenter l'héritage basé sur un prototype et des propriétés partagées. Les objets sont créés en
utilisant des constructeurs dans de nouvelles expressions; par exemple, new Date (2009,11) crée un nouvel objet Date. Invoquer un constructeur sans utiliser new a des conséquences qui dépendent du constructeur. Par exemple, Date () produit une représentation sous forme de chaîne de la date et de l'heure actuelles plutôt qu'un objet.

João Pinho
la source