Je me demande quelle est la meilleure façon de créer un objet JavaScript qui possède des propriétés et des méthodes.
J'ai vu des exemples où la personne a utilisé var self = this
puis utilise self.
dans toutes les fonctions pour s'assurer que la portée est toujours correcte.
Ensuite, j'ai vu des exemples d'utilisation .prototype
pour ajouter des propriétés, tandis que d'autres le font en ligne.
Quelqu'un peut-il me donner un bon exemple d'un objet JavaScript avec certaines propriétés et méthodes?
javascript
Michael Stum
la source
la source
self
un mot réservé? Sinon, cela devrait l'être; puisqu'ilself
s'agit d'une variable prédéfinie faisant référence à la fenêtre en cours.self === window
window
dans le modèle d'objet du navigateur commedocument
ouframes
; vous pouvez certainement réutiliser l'identifiant comme nom de variable. Bien que, oui, stylistiquement, je préfèrevar that= this
éviter toute confusion possible. Même siwindow.self
c'est finalement inutile donc il n'y a rarement aucune raison d'y toucher.this
à une variable locale (par exempleself
) réduit la taille des fichiers.Réponses:
Il existe deux modèles pour implémenter des classes et des instances en JavaScript: la méthode de prototypage et la méthode de fermeture. Les deux ont des avantages et des inconvénients, et il existe de nombreuses variantes étendues. De nombreux programmeurs et bibliothèques ont des approches et des fonctions utilitaires de gestion de classe différentes pour imprimer sur certaines des parties les plus laides du langage.
Le résultat est qu'en compagnie mixte, vous aurez un méli-mélo de métaclasses, toutes se comportant légèrement différemment. Pire encore, la plupart des didacticiels JavaScript sont terribles et servent de compromis intermédiaires pour couvrir toutes les bases, vous laissant très confus. (L'auteur est probablement aussi confus. Le modèle d'objet de JavaScript est très différent de la plupart des langages de programmation, et dans de nombreux endroits, il est mal conçu.)
Commençons par la manière prototype . C'est le plus natif de JavaScript que vous pouvez obtenir: il y a un minimum de code de surcharge et instanceof fonctionnera avec les instances de ce type d'objet.
Nous pouvons ajouter des méthodes à l'instance créée en les
new Shape
écrivant dans laprototype
recherche de cette fonction constructeur:Maintenant, pour le sous-classer, dans la mesure où vous pouvez appeler ce que JavaScript fait de la sous-classe. Nous le faisons en remplaçant complètement cette
prototype
propriété magique étrange :avant d'y ajouter des méthodes:
Cet exemple fonctionnera et vous verrez du code similaire dans de nombreux didacticiels. Mais l'homme, c'est
new Shape()
moche: nous instancions la classe de base même si aucune forme réelle n'est à créer. Il se trouve que cela fonctionne dans ce cas simple parce que JavaScript est tellement bâclé: il permet de passer zéro arguments, auquel casx
ety
devenirundefined
et sont assignés aux prototypesthis.x
etthis.y
. Si la fonction constructeur faisait quelque chose de plus compliqué, elle tomberait à plat sur son visage.Donc, ce que nous devons faire, c'est trouver un moyen de créer un objet prototype qui contient les méthodes et autres membres que nous voulons au niveau de la classe, sans appeler la fonction constructeur de la classe de base. Pour ce faire, nous allons devoir commencer à écrire du code d'aide. C'est l'approche la plus simple que je connaisse:
Cela transfère les membres de la classe de base dans son prototype vers une nouvelle fonction constructeur qui ne fait rien, puis utilise ce constructeur. Maintenant, nous pouvons écrire simplement:
à la place du
new Shape()
injustice. Nous avons maintenant un ensemble acceptable de primitives pour les classes construites.Il y a quelques améliorations et extensions que nous pouvons considérer sous ce modèle. Par exemple, voici une version syntaxique de sucre:
L'une ou l'autre version présente l'inconvénient que la fonction constructeur ne peut pas être héritée, comme c'est le cas dans de nombreux langages. Ainsi, même si votre sous-classe n'ajoute rien au processus de construction, elle doit se rappeler d'appeler le constructeur de base avec les arguments souhaités par la base. Cela peut être légèrement automatisé en utilisant
apply
, mais vous devez toujours écrire:Ainsi, une extension courante consiste à décomposer les éléments d'initialisation en sa propre fonction plutôt que le constructeur lui-même. Cette fonction peut alors très bien hériter de la base:
Nous avons maintenant le même passe-partout de fonction constructeur pour chaque classe. Peut-être pouvons-nous déplacer cela dans sa propre fonction d'assistance afin que nous n'ayons pas à le taper, par exemple au lieu de le
Function.prototype.subclass
retourner et laisser la fonction Function de la classe de base cracher des sous-classes:... qui commence à ressembler un peu plus à d'autres langages, mais avec une syntaxe légèrement plus maladroite. Vous pouvez saupoudrer quelques fonctionnalités supplémentaires si vous le souhaitez. Vous souhaitez peut-être
makeSubclass
prendre et mémoriser un nom de classe et fournir une valeurtoString
par défaut en l' utilisant. Peut-être que vous voulez que le constructeur détecte quand il a été accidentellement appelé sans l'new
opérateur (ce qui entraînerait souvent un débogage très ennuyeux):Peut-être que vous voulez passer tous les nouveaux membres et les
makeSubclass
ajouter au prototype, pour vous éviter d'avoir à écrireClass.prototype...
beaucoup. De nombreux systèmes de classe le font, par exemple:Il existe de nombreuses fonctionnalités potentielles que vous pourriez considérer comme souhaitables dans un système d'objets et personne n'est vraiment d'accord sur une formule particulière.
La fermeture , alors. Cela évite les problèmes d'héritage basé sur un prototype de JavaScript, en n'utilisant pas du tout l'héritage. Au lieu:
Désormais, chaque instance de
Shape
aura sa propre copie du fichiertoString
méthode (et toute autre méthode ou tout autre membre de classe que nous ajouterons).La mauvaise chose à propos de chaque instance ayant sa propre copie de chaque membre de la classe est qu'elle est moins efficace. Si vous avez affaire à un grand nombre d'instances sous-classées, l'héritage prototypique peut mieux vous servir. Appeler également une méthode de la classe de base est un peu ennuyeux comme vous pouvez le voir: nous devons nous souvenir de ce qu'était la méthode avant que le constructeur de la sous-classe ne l'écrase, sinon elle se perd.
[Aussi parce qu'il n'y a pas d'héritage ici, l'
instanceof
opérateur ne fonctionnera pas; vous devrez fournir votre propre mécanisme de reniflement de classe si vous en avez besoin. Bien que vous puissiez manipuler les objets prototypes de la même manière qu'avec l'héritage de prototypes, c'est un peu délicat et cela ne vaut pas vraiment la peine de se mettre auinstanceof
travail.]La bonne chose à propos de chaque instance ayant sa propre méthode est que la méthode peut alors être liée à l'instance spécifique qui la possède. Ceci est utile en raison de la façon étrange de liaison de JavaScript
this
dans les appels de méthode, qui a le résultat que si vous détachez une méthode de son propriétaire:puis
this
à l'intérieur de la méthode ne sera pas l'instance Circle comme prévu (ce sera en fait l'window
objet global , provoquant un malheur de débogage généralisé). En réalité, cela se produit généralement lorsqu'une méthode est prise et affectée à unsetTimeout
,onclick
ouEventListener
en général.Avec la méthode prototype, vous devez inclure une fermeture pour chaque mission de ce type:
ou, à l'avenir (ou maintenant si vous piratez Function.prototype), vous pouvez également le faire avec
function.bind()
:si vos instances sont effectuées de la manière de fermeture, la liaison est effectuée gratuitement par la fermeture sur la variable d'instance (généralement appelée
that
ouself
, bien que personnellement je déconseille cette dernière car elle aself
déjà une autre signification différente en JavaScript).1, 1
Cependant, vous ne recevez pas les arguments dans l'extrait ci-dessus gratuitement, vous aurez donc toujours besoin d'une autre fermeture ou d'unbind()
si vous avez besoin de le faire.Il existe également de nombreuses variantes de la méthode de fermeture. Vous pouvez préférer omettre
this
complètement, en créer un nouveauthat
et le renvoyer au lieu d'utiliser l'new
opérateur:Quelle voie est «appropriée»? Tous les deux. Quel est le «meilleur»? Cela dépend de votre situation. FWIW J'ai tendance à prototyper pour un véritable héritage JavaScript lorsque je fais fortement des trucs OO, et des fermetures pour de simples effets de page jetables.
Mais les deux façons sont assez contre-intuitives pour la plupart des programmeurs. Les deux ont de nombreuses variations potentiellement désordonnées. Vous rencontrerez les deux (ainsi que de nombreux schémas intermédiaires et généralement cassés) si vous utilisez le code / les bibliothèques d'autres personnes. Il n'y a pas de réponse généralement acceptée. Bienvenue dans le monde merveilleux des objets JavaScript.
[Cela fait partie 94 de Pourquoi JavaScript n'est pas mon langage de programmation préféré.]
la source
new
.J'utilise ce modèle assez fréquemment - j'ai trouvé qu'il me donne une assez grande flexibilité lorsque j'en ai besoin. En cours d'utilisation, il est assez similaire aux classes de style Java.
Celui-ci utilise une fonction anonyme appelée lors de la création, renvoyant une nouvelle fonction constructeur. Étant donné que la fonction anonyme n'est appelée qu'une seule fois, vous pouvez y créer des variables statiques privées (elles sont à l'intérieur de la fermeture, visibles par les autres membres de la classe). La fonction constructeur est fondamentalement un objet Javascript standard - vous définissez des attributs privés à l'intérieur et des attributs publics sont attachés à la
this
variable.Fondamentalement, cette approche combine l'approche Crockfordian avec des objets Javascript standard pour créer une classe plus puissante.
Vous pouvez l'utiliser comme vous le feriez pour tout autre objet Javascript:
la source
this
doit-il encore être instancié avecnew
?this
est utilisé dans laconstructor
fonction, qui devientFoo
dans l'exemple.constructor.prototype.myMethod = function () { ... }
.Douglas Crockford discute longuement de ce sujet dans The Good Parts . Il recommande d'éviter au nouvel opérateur de créer de nouveaux objets. Au lieu de cela, il propose de créer des constructeurs personnalisés. Par exemple:
En Javascript, une fonction est un objet et peut être utilisée pour construire des objets avec le nouvel opérateur. Par convention, les fonctions destinées à être utilisées comme constructeurs commencent par une majuscule. Vous voyez souvent des choses comme:
Si vous oubliez d'utiliser le nouvel opérateur lors de l' instanciation d' un nouvel objet, ce que vous obtenez est un appel de fonction ordinaire, et c'est lié à l'objet global plutôt que le nouvel objet.
la source
new
devar that = {};
toute façon.Pour continuer hors de la réponse de Bobince
Dans es6, vous pouvez maintenant réellement créer un
class
Alors maintenant, vous pouvez faire:
Alors, étendez-vous à un cercle (comme dans l'autre réponse), vous pouvez faire:
Finit un peu plus propre dans es6 et un peu plus facile à lire.
En voici un bon exemple en action:
Afficher l'extrait de code
la source
Vous pouvez également le faire de cette façon, en utilisant des structures:
Alors :
la source
Une autre façon serait http://jsfiddle.net/nnUY4/ (je ne sais pas si ce type de gestion de la création d'objets et des fonctions de révélation suit un modèle spécifique)
la source
Quand on utilise l'astuce de fermer sur "this" lors d'une invocation de constructeur, c'est pour écrire une fonction qui peut être utilisée comme rappel par un autre objet qui ne veut pas invoquer une méthode sur un objet. Ce n'est pas lié à "rendre la portée correcte".
Voici un objet JavaScript vanille:
Vous pourriez tirer beaucoup de la lecture de ce que Douglas Crockford a à dire sur JavaScript. John Resig est également brillant. Bonne chance!
la source
this
a tout à voir avec "rendre la portée correcte".self=this
bien qu'il ne force pasthis
à persister, permet facilement un cadrage "correct" via une fermeture.Closure
est polyvalent. bobince a bien résumé les approches prototype vs fermeture lors de la création d'objets. Cependant, vous pouvez imiter certains aspects de l'OOP
utilisation de la fermeture d'une manière de programmation fonctionnelle. N'oubliez pas que les fonctions sont des objets en JavaScript ; alors utilisez la fonction comme objet d'une manière différente.Voici un exemple de fermeture:
Il y a quelque temps, je suis tombé sur l'article de Mozilla sur la fermeture. Voici ce qui me saute aux yeux: "Une fermeture vous permet d'associer certaines données (l'environnement) à une fonction qui opère sur ces données. Cela a des parallèles évidents avec la programmation orientée objet, où les objets nous permettent d'associer certaines données (les propriétés de l'objet ) avec une ou plusieurs méthodes ". C'était la toute première fois que je lisais un parallélisme entre la fermeture et la POO classique sans référence au prototype.
Comment?
Supposons que vous souhaitiez calculer la TVA de certains articles. La TVA est susceptible de rester stable pendant la durée de vie d'une application. Une façon de le faire en POO (pseudo-code):
Fondamentalement, vous transmettez une valeur de TVA à votre constructeur et votre méthode de calcul peut l'utiliser via la fermeture . Maintenant, au lieu d'utiliser une classe / constructeur, passez votre TVA comme argument dans une fonction. Parce que la seule chose qui vous intéresse est le calcul lui-même, retourne une nouvelle fonction, qui est la méthode de calcul:
Dans votre projet, identifiez les valeurs de niveau supérieur qui sont un bon candidat de la TVA à calculer. En règle générale, chaque fois que vous transmettez les mêmes arguments, il existe un moyen de l'améliorer en utilisant la fermeture. Pas besoin de créer des objets traditionnels.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
la source
Création d'un objet
La façon la plus simple de créer un objet en JavaScript est d'utiliser la syntaxe suivante:
Cela fonctionne très bien pour stocker des données de manière structurée.
Pour les cas d'utilisation plus complexes, cependant, il est souvent préférable de créer des instances de fonctions:
Cela vous permet de créer plusieurs objets qui partagent le même «plan directeur», semblable à la façon dont vous utilisez les classes, par exemple. Java.
Cependant, cela peut être fait plus efficacement en utilisant un prototype.
Chaque fois que différentes instances d'une fonction partagent les mêmes méthodes ou propriétés, vous pouvez les déplacer vers le prototype de cet objet. De cette façon, chaque instance d'une fonction a accès à cette méthode ou propriété, mais elle n'a pas besoin d'être dupliquée pour chaque instance.
Dans notre cas, il est logique de déplacer la méthode
f
vers le prototype:Héritage
Un moyen simple mais efficace de faire l'héritage en JavaScript, consiste à utiliser la double ligne suivante:
Cela revient à faire ceci:
La principale différence entre les deux est que le constructeur de
A
n'est pas exécuté lors de l'utilisationObject.create
, ce qui est plus intuitif et plus similaire à l'héritage basé sur une classe.Vous pouvez toujours choisir d'exécuter éventuellement le constructeur de
A
lors de la création d'une nouvelle instance deB
en l'ajoutant au constructeur deB
:Si vous souhaitez passer tous les arguments de
B
àA
, vous pouvez également utiliserFunction.prototype.apply()
:Si vous souhaitez mélanger un autre objet dans la chaîne constructeur de
B
, vous pouvez combinerObject.create
avecObject.assign
:Démo
Remarque
Object.create
peut être utilisé en toute sécurité dans tous les navigateurs modernes, y compris IE9 +.Object.assign
ne fonctionne dans aucune version d'IE ni dans certains navigateurs mobiles. Il est recommandé de polyfillObject.create
et / ouObject.assign
si vous souhaitez les utiliser et prendre en charge les navigateurs qui ne les implémentent pas.Vous pouvez trouver un polyfill pour
Object.create
ici et un pourObject.assign
ici .la source
la source
En plus de la réponse acceptée de 2009. Si vous pouvez cibler des navigateurs modernes, vous pouvez utiliser Object.defineProperty .
Pour voir une liste de compatibilité de bureau et mobile, consultez la liste de compatibilité du navigateur de Mozilla . Oui, IE9 + le prend en charge ainsi que Safari mobile.
la source
Vous pouvez également essayer ceci
la source
Tout d'abord, vous pouvez modifier votre préférence en ajoutant des méthodes à l'instance au lieu de l'
prototype
objet du constructeur . Je déclare presque toujours des méthodes à l'intérieur du constructeur car j'utilise le détournement de constructeur très souvent le à des fins concernant l'héritage et les décorateurs.Voici comment je décide où les déclarations sont écrites:
this
)var
déclarations l'emporter surfunction
déclarations{}
et[]
)public
déclarations l'emporter surprivate
déclarationsFunction.prototype.bind
plusthus
,self
,vm
,etc
this
depuis la portée lexicale de l'espace de fermeture.Voici pourquoi ces aides:
Détournement de constructeur Conception du modèle Modèles de conceptionComme vous pouvez le voir, je n'en ai vraiment pas besoin
thus
car je préfèreFunction.prototype.bind
(ou.call
ou.apply
) plusthus
. Dans notreSingleton
classe, nous ne le nommons même pasthus
car ilINSTANCE
transmet plus d'informations. PourModel
, nous revenonsthis
afin de pouvoir invoquer le constructeur en utilisant.call
pour renvoyer l'instance que nous lui avons passée. De façon redondante, nous l'avons affecté à la variableupdated
, bien qu'il soit utile dans d'autres scénarios.Parallèlement, je préfère construire des objets littéraux en utilisant le
Préféré Pas préférénew
mot clé plutôt que {crochets}:Comme vous pouvez le voir, ce dernier n'a pas la possibilité de remplacer la propriété "override" de sa Superclasse.
Si j'ajoute des méthodes à l'
Préféré Pas préféréprototype
objet de la classe , je préfère un littéral d'objet - avec ou sans utiliser lenew
mot clé:la source
Je voudrais mentionner que nous pouvons utiliser un titre ou une chaîne pour déclarer un objet.
Il existe différentes manières d'appeler chaque type d'entre eux. Voir ci-dessous:
la source
Fondamentalement, il n'y a pas de concept de classe dans JS, nous utilisons donc la fonction comme un constructeur de classe qui est pertinent avec les modèles de conception existants.
Jusqu'à présent, JS n'a aucune idée que vous souhaitez créer un objet, alors voici le nouveau mot-clé.
Ref: JS professionnel pour les développeurs web - Nik Z
la source
class
dans JS, comme vous l'avez mentionné dans votre rubrique en utilisant lefunction
mot - clé. Ce n'est pas un modèle de conception mais une caractéristique intentionnelle de la langue. Je ne vous ai pas déçu à ce sujet, mais il semble que quelqu'un d'autre l'ait fait à cause de la justesse et de la non-pertinence de la question. J'espère que cette rétroaction vous aidera.