43 votes

Prototype de l'OO en JavaScript

TL;DR:

Avons-nous besoin d'usines/constructeurs de prototype OO? Peut-on faire un paradigme de l'interrupteur et de les déposer complètement?

L'Histoire:

J'ai pensé à faire le prototype de l'OO en JavaScript dernièrement, et de trouver que 99% de OO fait en JavaScript est forçage classique OO modèles en elle.

De mon point de vue sur le prototype de l'OO est qu'il implique deux choses. Un prototype statique de méthodes (et des données statiques) et une liaison de données. Nous n'avons pas besoin d'usines ou de constructeurs.

En JavaScript, ce sont des littéraux d'Objet contenant des fonctions et de l' Object.create.

Cela signifierait que nous pouvons modéliser tout comme statique plan/prototype et une liaison de données couche d'abstraction qui est de préférence accroché directement dans un document de style de base de données. I. e. les objets sont retirés de la base de données et créé par clonage d'un prototype avec les données. Cela signifierait il n'y a pas de logique constructeur, pas d'usines, pas d' new.

L'Exemple de code:

Un pseudo exemple serait :

var Entity = Object.create(EventEmitter, {
    addComponent: {
        value: function _addComponent(component) {
            if (this[component.type] !== undefined) {
                this.removeComponent(this[component.type]);
            }

            _.each(_.functions(component), (function _bind(f) {
                component[f] = component[f].bind(this);
            }).bind(this));

            component.bindEvents();

            Object.defineProperty(this, component.type, {
                value: component,
                configurable: true
            });

            this.emit("component:add", this, component);
        }
    },
    removeComponent: {
        value: function _removeComponent(component) {
            component = component.type || component;

            delete this[component];

            this.emit("component:remove", this, component);
        }
    }
}

var entity = Object.create(Entity, toProperties(jsonStore.get(id)))

Le mineur explication:

Le code est détaillé parce que ES5 est détaillé. Entity ci-dessus est un modèle/prototype. Tout objet réel avec des données pourraient être créés en utilisant Object.create(Entity, {...}).

Les données réelles (dans ce cas, les composants) est chargé directement à partir d'un JSON magasin et injecté directement dans l' Object.create appel. Bien sûr, un modèle similaire est appliquée à la création de composants et seules les propriétés qui passent Object.hasOwnProperty sont stockés dans la base de données.

Lorsqu'une entité est créée pour la première fois, il a créé avec un vide {}

Les Questions:

Maintenant mes questions sont

  • Open source des exemples de JS prototype OO?
  • Est-ce une bonne idée?
  • Est-il en ligne avec les idées et les concepts de la programmation orientée objet par prototype?
  • Ne va pas à l'aide de tout les constructeurs d'usine/ fonctions me mordre dans le cul quelque part? Peut-on vraiment s'en sortir sans l'aide des constructeurs. Il y a des restrictions à l'aide de la méthodologie décrite ci-dessus où nous aurions besoin d'usines pour les surmonter.

26voto

Ryan Kinal Points 8903

Je ne pense pas que le constructeur par défaut/logique est nécessaire à tous, aussi longtemps que vous changez la façon dont vous pensez à propos de la Programmation Orientée Objet. Dans ma récente exploration du sujet, j'ai découvert que l'héritage par Prototype se prête plus à la définition d'un ensemble de fonctions qui utilisent les données particulières. Ce n'est pas un concept étranger à personnes ayant reçu la formation classique de l'héritage, mais le hic, c'est que ces "parents" objets ne définissent pas les données pour être opéré.

var animal = {
    walk: function()
    {
        var i = 0,
            s = '';
        for (; i < this.legs; i++)
        {
            s += 'step ';
        }

        console.log(s);
    },
    speak: function()
    {
        console.log(this.favoriteWord);
    }
}

var myLion = Object.create(animal);
myLion.legs = 4;
myLion.favoriteWord = 'woof';

Ainsi, dans l'exemple ci-dessus, nous avons créer la fonctionnalité qui va de pair avec un animal, et ensuite créer un objet qui a cette fonctionnalité, ainsi que les données nécessaires pour compléter les actions. Cela se sent mal à l'aise et impair à tous ceux qui ont utilisé à la classique héritage, pour toute longueur de temps. Il n'en a pas le chaud, le flou de la public/privé/protégé de la hiérarchie de membre de la visibilité, et je serai le premier à admettre que ça me rend nerveux.

Aussi, mon premier réflexe, quand je vois le au-dessus de l'initialisation de l' myLion objet est de créer une usine pour les animaux, donc je peux créer des lions, des tigres et des ours (oh my) avec un simple appel de fonction. Et, je pense, c'est une façon naturelle de penser, pour la plupart des programmeurs - le niveau de verbosité du code ci-dessus est moche, et semble manquer d'élégance. Je n'ai pas encore décidé si c'est simplement dû à la formation classique, ou si c'est un réel défaut de la méthode ci-dessus.

Maintenant, sur l'héritage.

J'ai toujours entendu inhertance en JavaScript pour être difficile. Naviguer dans les tenants et les aboutissants de la chaîne de prototype n'est pas très clair. Jusqu'à ce que vous l'utilisez avec Object.create, qui prend toutes les fonction de base, nouveau-mot-clé de la redirection de l'équation.

Imaginons que nous voulions étendre la-dessus animal objet, et un humain.

var human = Object.create(animal)
human.think = function()
{
    console.log('Hmmmm...');
}

var myHuman = Object.create(human);
myHuman.legs = 2;
myHuman.favoriteWord = 'Hello';

Cela crée un objet qui a human comme un prototype, qui, à son tour, a animal comme un prototype. Assez facile. Pas de distraction, pas de "nouvel objet avec un prototype de l'égalité pour le prototype de la fonction". De simples prototypes héritage. C'est simple et commode. Le polymorphisme est facile, trop.

human.speak = function()
{
    console.log(this.favoriteWord + ', dudes');
}

En raison de la façon dont la chaîne de prototype fonctionne, myHuman.speak se trouvent dans human avant on en trouve dans animal, et c'est ainsi que notre homme est un surfeur au lieu de seulement un ennuyeux animal.

Donc, en conclusion (TLDR):

Le pseudo-classique constructeur fonctionnalité était cloué sur le langage JavaScript pour les programmeurs formés dans la musique classique, la programmation orientée objet plus à l'aise. Il n'est pas, par tout moyen, nécessaire, mais il est question d'abandonner les concepts classiques comme membre de la visibilité et (de façon tautologique) des constructeurs.

Ce que vous obtenez en retour est la flexibilité et la simplicité. Vous pouvez créer des "classes" à la volée - chaque objet est, en soi, un modèle pour d'autres objets. Les valeurs de réglage sur les objets enfants n'affectera pas le prototype de ces objets (c'est à dire si j'ai utilisé var child = Object.create(myHuman), puis définissez child.walk = 'not yet', animal.walk seraient pas affectés - vraiment, à tester).

La simplicité de l'héritage est honnêtement ahurissant. J'ai lu beaucoup de choses sur l'héritage en JavaScript, et écrit beaucoup de lignes de code de tenter de le comprendre. Mais ça se résume à des objets héritent à partir d'autres objets. C'est aussi simple que ça, et tous l' new mot clé n'est la confusion que jusqu'à.

Cette souplesse est difficile à utiliser à sa pleine mesure, et je suis sûr que j'ai encore à faire, mais il est là, et il est intéressant de naviguer. Je pense que la plupart de la raison qu'il n'a pas été utilisé pour un grand projet, c'est qu'il n'est tout simplement pas bien compris qu'il pourrait l'être, et, à mon humble avis, nous sommes enfermés dans la classique héritage des modèles nous avons tous appris lorsque nous avons appris le C++, Java, etc.

Modifier

Je pense que j'ai fait une assez bonne affaire à l'encontre des constructeurs. Mais mon argument contre les usines est floue.

Après plus de contemplation, au cours de laquelle j'ai basculé sur les deux côtés de la clôture, à plusieurs reprises, je suis parvenu à la conclusion que les usines sont également inutiles. Si animal (ci-dessus) a donné une autre fonction initialize, il serait trivial pour créer et initialiser un objet qui hérite de animal.

var myDog = Object.create(animal);
myDog.initialize(4, 'Meow');

Nouvel objet, initialisé et prêt à l'emploi.

@Raynos - Vous totalement nerd sniped de moi sur cet un. Je devrais être prêt pour les 5 jours de faire absolument rien de productif.

11voto

davin Points 19682

Que par votre commentaire que la question est surtout "est constructeur de connaissances nécessaires?" Je sens qu'il est.

Un jouet exemple serait de stocker partielle des données. Sur un jeu de données en mémoire, lors de la persistance je ne peut choisir de stocker certains éléments (que ce soit pour des raisons d'efficacité ou de la cohérence des données, par exemple les valeurs sont inutiles une fois persistant). Prenons une session où je stocker le nom d'utilisateur et le nombre de fois qu'ils ont cliqué sur le bouton d'aide (par manque d'un meilleur exemple). Lorsque je persiste dans mon exemple, je ne l'ai pas utiliser pour le nombre de clics, depuis je le garde en mémoire maintenant, et la prochaine fois que je charge les données (la prochaine fois que l'utilisateur se connecte ou se connecte ou quoi que ce soit), je vais procéder à l'initialisation de la valeur à partir de zéro (sans doute à 0). Ce cas d'utilisation est un bon candidat pour la logique constructeur.

Aahh, mais vous pouvez toujours juste l'incorporer dans la statique prototype: Object.create({name:'Bob', clicks:0}); Sûr, dans ce cas. Mais que faire si la valeur n'était pas toujours à 0 au premier abord, mais c'était plutôt quelque chose qui calcul demandé. Uummmm, par exemple, les utilisateurs de l'âge en secondes (en supposant que nous avons stocké le nom et la date de naissance). Encore une fois, un élément qu'il est de peu d'usage persistant, car il devra être recalculé sur la récupération de toute façon. Alors, comment avez-vous stocker de l'âge de la statique prototype?

La réponse est évidente constructeur/initialiser la logique.

Il y a beaucoup plus de scénarios, bien que je ne me sens pas l'idée est liée à js de la programmation orientée objet ou n'importe quelle langue en particulier. La nécessité de la création d'entités de la logique inhérente à la façon dont je vois les systèmes informatiques de modèle dans le monde. Parfois, les articles que nous magasin sera un simple extraction et d'injection dans un plan, comme prototype shell, et parfois les valeurs sont dynamiques, et a besoin d'être initialisé.

Mise à JOUR

OK, je vais essayer pour un exemple plus réaliste, et pour éviter toute confusion supposer que je n'ai pas de base de données et n'a pas besoin de persister les données. Disons que je suis en train de faire un solitaire serveur. Chaque nouveau jeu sera (évidemment) une nouvelle instance de l' Game prototype. Il est clair pour moi que leur est initialiser la logique nécessaire (et beaucoup d'elle):

Je vais, par exemple, ont besoin sur chaque jeu et pas seulement statique/codé en dur jeu de cartes, mais un mélangées au hasard pont. Si elle était statique de l'utilisateur pourrait jouer le même jeu à chaque fois, ce qui n'est clairement pas bon.

J'ai peut-être également démarrer une minuterie pour finir le jeu si le joueur est à court. Encore une fois, pas quelque chose qui peut être statique, depuis que mon jeu a quelques exigences: le nombre de secondes est inversement proportionnel au nombre de jeux connectés joueur a remporté à ce jour (encore une fois, pas d'infos enregistrées, juste que pour cette connexion), et proportionnel à la difficulté de la lecture aléatoire (il existe un algorithme qui, selon le shuffle résultats peuvent déterminer le degré de difficulté du jeu).

Comment faites-vous cela avec un statique Object.create()?

2voto

jcolebrand Points 11824

Exemple d'un staticly-clonable "Type":

var MyType = {
  size: Sizes.large,
  color: Colors.blue,
  decay: function _decay() { size = Sizes.medium },
  embiggen: function _embiggen() { size = Sizes.xlarge },
  normal: function _normal() { size = Sizes.normal },
  load: function _load( dbObject ) { 
    size = dbObject.size
    color = dbObject.color 
  }
}

Maintenant, nous pourrions clone de ce type ailleurs, oui? Bien sûr, nous aurions besoin d'utiliser var myType = Object.Create(MyType), mais ensuite c'est fini, oui? Maintenant, il suffit de myType.size et de la taille de la chose. Ou nous pourrions lire la couleur, ou la modifier, etc. Nous n'avons pas créé un constructeur ou quoi que ce soit, non?

Si vous l'avez dit il n'y a pas de constructeur de là, vous avez tort. Laissez-moi vous montrer où le constructeur est:

// The following var definition is the constructor
var MyType = {
  size: Sizes.large,
  color: Colors.blue,
  decay: function _decay() { size = Sizes.medium },
  embiggen: function _embiggen() { size = Sizes.xlarge },
  normal: function _normal() { size = Sizes.normal },
  load: function _load( dbObject ) { 
    size = dbObject.size
    color = dbObject.color 
  }
}

Parce que nous avons déjà allé et a créé toutes les choses que nous voulions et nous avons déjà défini tout. C'est tout un constructeur ne. Donc, même si nous ne clone/utilisation statique des choses (qui est ce que je vois ci-dessus des extraits de le faire), nous avons toujours eu un constructeur. Juste un constructeur statique. Par définition d'un type, nous avons défini un constructeur. L'alternative est de ce modèle de construction de l'objet:

var MyType = {}
MyType.size = Sizes.large

Mais finalement vous allez avoir à utiliser l'Objet.Créer(MyType) et lorsque vous le faites, vous avez utilisé un objet statique pour créer l'objet cible. Et ensuite, elle devient la même que dans l'exemple précédent.

2voto

Dino Gambone Points 270

La réponse courte à votre question "avons-nous besoin d'usines/constructeurs de prototype OO?" est non. Usines/Constructeurs de servir 1 seul but: initialiser l'objet nouvellement créé (une instance) à un état spécifique.

Cela étant dit, il est souvent utilise car certains objets ont besoin de code d'initialisation d'une certaine sorte.

Nous allons utiliser le composant de base de l'entité de code que vous avez fournis. Une entité typique est simplement une collection de composants et de quelques propriétés:

var BaseEntity = Object.create({},
{
    /* Collection of all the Entity's components */
    components:
    {
        value: {}
    }

    /* Unique identifier for the entity instance */
    , id:
    {
        value: new Date().getTime()
        , configurable: false
        , enumerable: true
        , writable: false
    }

    /* Use for debugging */
    , createdTime:
    {
        value: new Date()
        , configurable: false
        , enumerable: true
        , writable: false
    }

    , removeComponent:
    {
        value: function() { /* code left out for brevity */ }
        , enumerable: true
        , writable: false
    }

    , addComponent:
    {
        value: function() { /* code left out for brevity */ }
        , enumerable: true
        , writable: false
    }
});

Maintenant, le code suivant va créer de nouvelles entités basée sur le "BaseEntity'

function CreateEntity()
{
    var obj = Object.create(BaseEntity);

    //Output the resulting object's information for debugging
    console.log("[" + obj.id + "] " + obj.createdTime + "\n");

    return obj;
}

Semble assez clair, jusqu'à ce que vous allez pour les propriétés:

setTimeout(CreateEntity, 1000);
setTimeout(CreateEntity, 2000);
setTimeout(CreateEntity, 3000);

sorties:

[1309449384033] Thu Jun 30 2011 11:56:24 GMT-0400 (EDT)
[1309449384033] Thu Jun 30 2011 11:56:24 GMT-0400 (EDT)
[1309449384033] Thu Jun 30 2011 11:56:24 GMT-0400 (EDT)

Alors pourquoi est-ce? La réponse est simple: parce que d'un prototype basé sur l'héritage. Lorsque nous avons créé les objets, il n'y avait pas de code pour définir les propriétés de l' id et createdTime sur l'instance elle-même, comme cela est généralement fait dans les constructeurs/usines. En conséquence, lors de l'accès de la propriété, il sort de la chaîne de prototype, qui finit par être une valeur unique pour toutes les entités.

L'argument est que l'Objet.créer() doit être passée en second paramètre à définir ces valeurs. Ma réponse serait tout simplement: N'est-ce pas fondamentalement la même que l'appel à un constructeur ou à l'aide d'une usine? C'est juste une autre manière de mettre de l'état d'un objet.

Maintenant, avec votre application où vous traiter (et à juste titre) tous les prototypes comme un ensemble de méthodes et propriétés statiques, vous ne initialiser l'objet en attribuant les valeurs des propriétés des données provenant d'une source de données. Il peut ne pas être l'aide d' new ou un certain type de l'usine, mais c'est le code d'initialisation.

Pour résumer: En JavaScript prototype de la programmation orientée objet - new n'est pas nécessaire - Les usines ne sont pas nécessaires - Code d'initialisation est généralement nécessaire, ce qui est normalement fait par le biais new, les usines, ou d'une autre mise en œuvre que vous ne voulez pas l'admettre est en cours d'initialisation d'un objet

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X