38 votes

Pas moyen d'avoir des objets de classe en javascript?

Le javascript prototype de base de la programmation orientée objet style est intéressant, mais il y a beaucoup de situations où vous avez besoin de la capacité à créer des objets à partir d'une classe.

Par exemple, dans une application de dessin vectoriel, l'espace de travail sera généralement vide au début du dessin : je ne peux pas créer une nouvelle "ligne" à partir d'un existant. Plus généralement, toute situation où les objets sont créés dynamiquement nécessitent l'utilisation de classes.

J'ai lu beaucoup de tutoriels et le livre "Javascript : les bonnes parties", mais pourtant il me semble qu'il n'y a aucun moyen de définir des classes qui respectent 1) l'encapsulation et 2) efficace, membre de la déclaration de méthodes (je veux dire : membre des méthodes qui sont définies à la fois, et partagée entre toutes les instances de classe).

Pour définir des variables privées, les fermetures sont utilisés :

function ClassA()
{
    var value = 1;
    this.getValue = function()
    {
        return value;
    }
}

Le problème ici est que chaque instance de "ClassA" aura sa propre copie de la fonction membre "getValue", ce qui n'est pas efficace.

Pour définir les fonctions de membre de manière efficace, le prototype est utilisé :

function ClassB()
{
    this.value = 1;
}

ClassB.prototype.getValue = function()
{
    return this.value;
}

Le problème ici est que la variable de membre de la "valeur" est public.

Je ne pense pas que ce problème peut être résolu facilement, puisque "privé", les variables doivent être définies LORS de la création de l'objet (de sorte que l'objet peut avoir accès à son contexte de création, sans s'exposer à ces valeurs) alors que basé sur des prototypes de fonctions de membres de définition doit être fait APRÈS la création de l'objet, de sorte que le prototype de sens ("ce.prototype" n'existe pas, j'ai vérifié).

Ou ai-je raté quelque chose ?


EDIT :

Tout d'abord, merci pour vos réponses intéressantes.

Je voulais juste ajouter une petite précision à mon message initial :

Ce que je veux vraiment faire est de 1) variables privées (encapsulation est bon, parce que les gens n'ont accès à ce dont ils ont besoin) et 2) efficace, membre de la déclaration de méthodes (éviter les copies).

Il semble que de simples variables privées déclaration ne peut réellement être atteint grâce à la fermeture en javascript, c'est pourquoi je me concentre sur la classe de base de l'approche. Si il existe un moyen de parvenir à de simples variables privées déclaration avec un prototype à base d'approche, c'est bon pour moi, je ne suis pas une acharnée de la classe de base de l'approche proponnent.

Après avoir lu les réponses, il semble que la solution la plus simple est d'oublier que les soldats, et l'utilisation spéciale des conventions de codage pour detter d'autres programmeurs d'accéder à "privé", les variables directement...

Et je suis d'accord, mon titre et la première phrase est trompeuse quant à la question que je voulais discuter ici.

31voto

Benjamin Gruenbaum Points 51406

Shh, venez ici! Vous voulez entendre un secret?

Classique héritage est testé et essayé approche.

Il est utile de le mettre en œuvre en JavaScript souvent. Les Classes sont un beau concept, d'avoir et d'avoir des modèles pour la modélisation de notre monde des objets est génial.

Classique héritage est juste un modèle. Il est parfaitement OK pour mettre en œuvre classique de l'héritage en JavaScript si c'est le modèle dont vous avez besoin pour votre cas d'utilisation.

L'héritage par prototype se concentre sur le partage de la fonctionnalité et c'est génial (dinasaur pilon génial), mais dans certains cas, vous souhaitez partager des données système et non pas de la fonctionnalité. C'est un problème de l'héritage par prototype ne s'adresse pas à tous.

Donc, vous me dites classes ne sont pas mauvais comme tout le monde continue à me dire?

Non, ils ne le sont pas. Ce que le JS de la communauté fronce les sourcils sur n'est pas le concept de classes, c'est de te limiter à de simples cours pour la réutilisation du code. Tout comme la langue ne pas appliquer de solides ou de typage statique, il n'a pas d'appliquer les schémas de la structure de l'objet.

En fait, derrière la scène intelligente implémentations de la langue peut transformer vos objets normaux à quelque chose ressemblant à de la classique héritage de classes.

Alors, comment faire fonctionner des classes en JavaScript

Eh bien, vous avez vraiment besoin d'un constructeur:

function getVehicle(engine){
    return { engine : engine };
}

var v = getVehicle("V6");
v.engine;//v6

Nous avons maintenant une classe de véhicules. Nous n'avons pas besoin de définir une catégorie de Véhicules explicitement à l'aide d'un mot-clé spécial. Maintenant, certaines personnes n'aiment pas faire les choses de cette façon et sont utilisés à la plus classique. Pour ce JS fournit (stupide à mon humble avis) sucre syntaxique en faisant:

function Vehicle(engine){
     this.engine = engine;
}
var v = new Vehicle("V6");
v.engine;//v6

C'est la même chose que l'exemple ci-dessus, pour la plupart.

Alors, que sommes-nous en manque encore?

L'héritage et les membres privés.

Et si je vous disais base de sous-typage est très simple en JavaScript?

JavaScript de la notion de typage est différent de ce que nous sommes habitués dans d'autres langues. Que signifie être un sous-type d'un type en JS?

var a = {x:5};
var b = {x:3,y:3};

Est le type d' b d'un sous-type du type d' a? Disons que si c'est en fonction de (forte) de sous-typage comportemental (LSP):

<<<< Commencer la partie technique

  • La Contravariance des arguments de méthode dans le sous - type Est conservé intégralement dans cette sorte d'héritage.
  • La Covariance des types de retour dans le sous - type Est conservé intégralement dans cette sorte d'héritage.
  • Pas de nouvelles exceptions doivent être levées par les méthodes de la sous-type, sauf si ces exceptions sont eux-mêmes des sous-types d'exceptions générées par les méthodes de la supertype. - Est conservé intégralement dans cette sorte d'héritage.

Aussi,

Tous ces sont encore, sont à nous de la maintenir. Nous pouvons les garder serrée ou faiblement comme nous le voulons, nous n'avons pas, mais nous ne pouvons certainement.

Question de fait, aussi longtemps que nous nous conformons à ces règles ci-dessus lors de la mise en œuvre de notre héritage, nous sommes pleinement en œuvre forte de comportement de sous-typage, qui est une forme très puissante de sous-typage (voir note*).

>>>>> Fin de la partie technique

Trivialement, on peut aussi voir que les sous-typage est titulaire.

Comment cela s'applique à notre Car exemple?

function getCar(typeOfCar){
    var v = getVehicle("CarEngine");
    v.typeOfCar = typeOfCar;
    return v;
}
v = getCar("Honda");
v.typeOfCar;//Honda;
v.engine;//CarEngine

Pas trop dur, était-il? Qu'en est privé?

function getVehicle(engine){
    var secret = "Hello"
    return {
        engine : engine,
        getSecret : function() {
            return secret;
        }
    };
}

Voir, secret est une fermeture variable. Il est parfaitement "privé", il fonctionne différemment des soldats dans des langues comme Java, mais il est impossible d'accéder de l'extérieur.

Ce sujet d'avoir des soldats dans les fonctions?

Ah! C'est une excellente question.

Si nous voulons utiliser une variable privée dans une fonction que nous partageons sur le prototype que nous avons besoin de firrst comprendre comment JS fermetures et les fonctions de travail.

Dans les fonctions JavaScript sont de première classe. Cela signifie que vous pouvez passer des fonctions de autour de.

function getPerson(name){
    var greeting = "Hello " + name;
    return {
        greet : function() {
            return greeting;
        }
    };
}

var a = getPerson("thomasc");
a.greet(); //Hello thomasc

C'est très bien, mais nous ne pouvons passer à la fonction bornée à un autour de à d'autres objets! Cela vous permet de le faire très lâche de découplage qui est génial.

var b = a.greet;
b(); //Hello thomasc

Attendez! Comment avez - b savoir le nom de la personne est thomasc? C'est juste la magie de fermetures. Assez impressionnant hein?

Vous pourriez être inquiet au sujet de la performance. Permettez-moi de vous dire comment j'ai appris à cesser de s'inquiéter et commencé à aimer l'optimisation de JIT.

Dans la pratique, à faire des copies de fonctions comme cela n'est pas un gros problème. Des fonctions en javascript sont tous à peu près bien, la fonctionnalité! Les fermetures sont un concept impressionnant, une fois que vous saisir et à les maîtriser vous voyez, c'est bien la peine, et le gain de performance n'est pas vraiment significatif. JS est de plus en plus rapides tous les jours, ne vous inquiétez pas au sujet de ces sortes de problèmes de performances.

Si vous pensez que c'est compliqué, la suite est aussi très légitime. Un contrat commun avec d'autres développeurs simplement dit "Si ma variable commence par _ de ne pas y toucher, nous sommes deux adultes consentants". Cela ressemblerait à quelque chose comme:

function getPerson(name){
    var greeter = {
        greet : function() {
            return "Hello" +greeter._name;
        }
    };
    greeter._name = name;
    return greeter;
}

Ou dans le style classique

function Person(name){
    this._name = name;
    this.greet = function(){
       return "Hello "+this._name;
    }
}

Ou si vous le souhaitez cache de la fonction sur le prototype au lieu d'instancier des copies:

function Person(name){
    this._name = name;
}
Person.prototype.greet =  function(){
       return "Hello "+this._name;
}

Donc, pour résumer:

  • Vous pouvez utiliser la classique héritage des formes, ils sont utiles pour le partage de types de données

  • Vous devez également utiliser l'héritage par prototype, il est tout aussi puissant, et beaucoup plus dans le cas où vous voulez partager de la fonctionnalité.

  • TheifMaster trompé. Avoir des soldats privé n'est vraiment pas une grosse affaire que l'on pourrait penser en JavaScript, aussi longtemps que votre code définit une interface claire cela ne devrait pas être problématique à tous. Nous sommes tous concenting adultes ici :)

*L'habile lecteur pourrait penser: Hein? N'étiez-vous pas tromper moi là-bas avec l'histoire de la règle? Je veux dire, l'accès à la propriété n'est pas encapsulé.

Je dis non, je n'étais pas. Même si vous n'avez pas explicitement encapsuler les domaines comme le secteur privé, vous pouvez simplement définir votre contrat dans un sens qui n'a pas accès à eux. Souvent comme TheifMaster suggéré avec _. Aussi, je pense que l'histoire de la règle n'est pas que les grandes d'un accord dans un grand nombre de ces scénarios, tant que nous ne sommes pas changer la façon dont l'accès à la propriété traite des propriétés de l'objet parent. Encore une fois, c'est à nous.

28voto

Aadit M Shah Points 17951

Je ne veux pas décourager puisque vous semblez être un phénomène relativement nouveau membre de StackOverflow, cependant, je vais être un peu dans votre visage et dire que c'est une très mauvaise idée d'essayer de mettre en œuvre classique de l'héritage en JavaScript.

Note: Quand je dis que c'est une mauvaise idée pour mettre en œuvre classique de l'héritage en JavaScript, je veux dire qu'en essayant de simuler effectif des classes, des interfaces, des modificateurs d'accès, etc. en JavaScript est une mauvaise idée. Néanmoins, la classique héritage comme un modèle de conception dans le JavaScript est utile car c'est juste du sucre syntaxique pour les prototypes héritage ( au maximum minimal de classes). J'utilise ce modèle dans mon code tout le temps (à la compléter).

JavaScript est un prototypes langage de programmation orienté objet. Pas un classique, langage de programmation orienté objet. Bien sûr, vous pouvez mettre en œuvre classique de l'héritage sur le dessus de JavaScript, mais avant de vous faire garder les choses suivantes à l'esprit:

  1. Vous allez à l'encontre de l'esprit de la langue, ce qui signifie que vous serez confrontés à des problèmes. Beaucoup de problèmes de performance, de lisibilité, de maintenance, etc.
  2. Vous n'avez pas besoin classes. Thomas, je sais que vous croyez vraiment que vous avez besoin les classes, mais faites-moi confiance sur ce point. Vous n'avez pas.

Pour ton bien, je vais vous donner deux réponses à cette question. Le premier va vous montrer comment faire de la classique héritage en JavaScript. Le second (que je recommande) va vous apprendre à embrasser prototypes héritage.

Classique Héritage en JavaScript

La plupart des programmeurs commencer par essayer de mettre en œuvre classique de l'héritage en JavaScript. Même JavaScript Gourous comme Douglas Crockford a essayé de mettre en œuvre classique de l'héritage en JavaScript. J'ai aussi essayé de mettre en œuvre classique de l'héritage en JavaScript.

J'ai d'abord créé une bibliothèque appelée sur des roulettes et ensuite augmenter. Cependant, je ne vous recommande pas d'utiliser l'une de ces bibliothèques, parce qu'elles vont à l'encontre de l'esprit de JavaScript. La vérité est que j'étais encore un amateur des programmeurs JavaScript quand j'ai écrit ces classique héritage des bibliothèques.

La seule raison pour laquelle j'ai mentionné que c'est parce que tout le monde est un amateur qui à un moment donné du temps, et bien que je préférerais que vous n'utilisez pas classique modèles d'héritage en JavaScript, je ne peux pas attendre de vous que vous comprenez pourquoi les prototypes des questions d'héritage pour l'instant.

Vous ne pouvez pas apprendre à faire du vélo sans tomber à quelques reprises. Je crois que vous êtes encore en phase d'apprentissage à l'égard de prototypes à l'héritage. Votre besoin de la classique héritage, c'est comme les roues d'entraînement sur les cycles.

Néanmoins, des roues de formation sont importants. Si vous voulez il y a quelques classique héritage bibliothèques qui devrait vous rendre plus à l'aise à l'écriture de code en JavaScript. Une telle bibliothèque est jTypes. Juste n'oubliez pas d'enlever les roues d'entraînement lorsque vous êtes sûr de vos compétences en tant que programmeur JavaScript.

Note: Personnellement, je n'aime pas jTypes un peu.

Prototypes Héritage en JavaScript

Je suis en train d'écrire cet article comme une étape importante pour vous, de sorte que vous pouvez revenir plus tard et de savoir quoi faire quand vous êtes prêt à apprendre vrai prototypes de l'héritage.

Tout d'abord, la ligne suivante est incorrecte:

Le javascript prototype de base de la programmation orientée objet style est intéressant, mais il y a beaucoup de situations où vous avez besoin de la capacité à créer des objets à partir d'une classe.

C'est faux parce que:

  1. Vous n'aurez plus jamais besoin de créer des objets à partir d'une classe en JavaScript.
  2. Il n'y a aucun moyen de créer une classe en JavaScript.

Oui, il est possible de simuler la classique héritage en JavaScript. Cependant, vous avez encore de l'héritage de propriétés des objets et non des classes. Par exemple, ECMAScript cours d'Harmonie sont tout sucre syntaxique pour le modèle classique de prototypes à l'héritage.

Dans le même contexte, l'exemple que vous avez donné est faux aussi:

Par exemple, dans une application de dessin vectoriel, l'espace de travail sera généralement vide au début du dessin : je ne peux pas créer une nouvelle "ligne" à partir d'un existant. Plus généralement, toute situation où les objets sont créés dynamiquement nécessitent l'utilisation de classes.

Oui, vous pouvez créer une nouvelle ligne à partir d'un existant, même si l'espace de travail est vide au début. Ce que vous devez comprendre, c'est que la ligne n'est pas en fait tiré bien.

var line = {
    x1: 0,
    y1: 0,
    x2: 0,
    y2: 0,
    draw: function () {
        // drawing logic
    },
    create: function (x1, y1, x2, y2) {
        var line = Object.create(this);
        line.x1 = x1;
        line.y1 = y1;
        line.x2 = x2;
        line.y2 = y2;
        return line;
    }
};

Maintenant, vous pouvez dessiner votre la ligne ci-dessus en communiquant line.draw ou autre chose, vous pouvez créer une nouvelle ligne à partir de ça:

var line2 = line.create(0, 0, 0, 100);
var line3 = line.create(0, 100, 100, 100);
var line4 = line.create(100, 100, 100, 0);
var line5 = line.create(100, 0, 0, 0);

line2.draw();
line3.draw();
line4.draw();
line5.draw();

Les lignes line2, line3, line4 et line5 forme d'un 100x100 place quand élaboré.

Conclusion

Donc, vous voyez que vous n'avez vraiment pas besoin classes en JavaScript. Les objets sont assez. L'Encapsulation peut être facilement réalisé à l'aide de fonctions.

Cela étant dit, vous ne pouvez pas avoir les fonctions publiques de chaque instance de l'accès privé de l'état de l'objet, sans que chaque instance ayant son propre ensemble de fonctions publiques.

Ce n'est pas un problème-mais parce que:

  1. Vous n'avez pas vraiment besoin privé de l'état. Vous pouvez penser que vous le faites, mais vous n'avez pas vraiment.
  2. Si vous voulez vraiment faire une variable privée alors que ThiefMaster mentionné juste préfixe le nom de la variable avec un trait de soulignement et de dire à vos utilisateurs de ne pas l'abîmer.

4voto

elclanrs Points 40467

Aight, voici ma tentative de résoudre ce problème particulier, même si je pense que le respect des conventions est une meilleure approche, à savoir. préfixez vos variables avec _ . Ici, je ne fais que suivre les instances d'un tableau, elles peuvent ensuite être supprimées avec une méthode _destroy . Je suis sûr que cela peut être amélioré, mais j'espère que cela vous donnera quelques idées:

 var Class = (function ClassModule() {

  var private = []; // array of objects of private variables

  function Class(value) {
    this._init();
    this._set('value', value);
  }

  Class.prototype = {

    // create new instance
    _init: function() {
      this.instance = private.length;
      private.push({ instance: this.instance });
    },

    // get private variable
    _get: function(prop) {
      return private[this.instance][prop];
    },

    // set private variable
    _set: function(prop, value) {
      return private[this.instance][prop] = value;
    },

    // remove private variables
    _destroy: function() {
      delete private[this.instance];
    },

    getValue: function() {
      return this._get('value');
    }
  };

  return Class;
}());

var a = new Class('foo');
var b = new Class('baz');

console.log(a.getValue()); //=> foo
console.log(b.getValue()); //=> baz

a._destroy();

console.log(b.getValue()); //=> baz
 

2voto

Esailija Points 74052

Vous n'avez pas besoin de privé/public au runtime. Ce sont exécutoires de manière statique. Tout projet suffisamment complexe pour faire respecter les propriétés privées ne sont pas utilisés à l'extérieur aura un build/pré-étape de processus que vous pouvez utiliser pour vérifier le fait. Même dans des langues avec la syntaxe privé/public disposent d'un moyen d'accès privé à l' exécution.

Comme pour la définition de la classe de base des objets, le constructeur+prototype que vous utilisez est la plus simple et la plus efficace. Plus de magie va être plus complexe et moins performant.

Bien que vous pouvez mettre en cache prototype de sorte que vous n'avez pas à répéter ClassB.prototype. tout le temps:

//in node.js you can leave the wrapper function out
var ClassB = (function() {
    var method = ClassB.prototype;

    function ClassB( value ) {
        this._value = value;
    }

    method.getValue = function() {
        return this._value;
    };

    method.setValue = function( value ) {
        this._value = value;
    };

    return ClassB;
})();

Le ci-dessus ne nécessite pas de bibliothèque et vous pouvez facilement créer une macro pour cela.

Aussi, dans ce cas, même une expression régulière est suffisante pour vérifier que "privé" propriétés sont utilisées correctement. Exécutez /([a-zA-Z$_-][a-zA-Z0-9$_-]*)\._.+/g le fichier et de voir que le premier match est toujours this. http://jsfiddle.net/7gumy/

1voto

Jon Koops Points 606

Il est impossible pour autant que je sais, sans d'autres instances sur la valeur, donc si c'est une constante, tu es encore bien en l'enveloppant dans une fonction comme ceci:

(function( context ) {

    'use strict';

    var SOME_CONSTANT = 'Hello World';

    var SomeObject = function() {};

    SomeObject.prototype.sayHello = function() {
        console.log(SOME_CONSTANT);
    };

    context.SomeObject = SomeObject;

})( this );

var someInstance = new SomeObject();
someInstance.sayHello();

Le mieux que vous pourriez faire est d'annoter qu'une propriété ne devrait pas être touché par l'aide d'un trait de soulignement comme l' this._value au lieu de this.value.

Notez que les fonctions privées sont possibles en les cachant dans une fonction:

(function( context ) {

    'use strict';

    var SomeObject = function() {};

    var getMessage = function() {
        return 'Hello World';
    };

    SomeObject.prototype.sayHello = function() {
        console.log(getMessage());
    };

    context.SomeObject = SomeObject;

})( this );

var someInstance = new SomeObject();
someInstance.sayHello();

Voici un exemple de 2 'Classes' étendre et d'interagir les uns avec les autres: http://jsfiddle.net/TV3H3/

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