42 votes

Comment implémenter les modificateurs d'accès C# en javascript ?

  • Résumé

    J'ai essayé de réaliser l'héritage et l'encapsulation correctement en javascript comme dans un langage basé sur les classes tel que c#.

    Le problème est que les membres protégés ont plusieurs copies dans les instances privées qui ne sont accessibles que par la fermeture, et je n'ai pas d'autre idée que de rafraîchir ces membres dans les instances privées.

    Si c'est possible, je veux me débarrasser des deux transmit y transfer dans mon code de Function.extend .

  • Mise à jour Pour les personnes intéressées par la citation ou la recherche, voici le dépôt du code source :

    https://github.com/kenkins/Function.extend

  • L'histoire

    Desde assemblages peut être un concept qui n'est pas à la portée de javascript, je ne prends pas l'option de l'utiliser. internal en tenant compte du modificateur, mais public , protected y private .

    public y private ne sont pas si difficiles à réaliser, mais avec l'héritage, protected est très délicate. Pourtant, ce n'est pas une chose recommandée à faire avec javascript, la plupart des articles que j'ai lus disent avec un caractère spécial et le documenter .

    Mais il semble que je persiste à faire du javascript pour simuler les langages à base de classes je stole cette idée et mis en œuvre à ma façon, le code se trouve à la fin de cet article.

    L'idée derrière la scène est de placer une accessibilité plus élevée avec un prototype plus élevé et d'accéder au prototype le plus élevé avec une fermeture.

    Supposons que nous ayons trois prototypes A , D y G Il semble que

    BANxt.png

    Comme il n'est pas possible qu'un objet soit une instance d'un type et d'un autre type qui n'est pas dans la chaîne de prototypes, la méthode que j'ai choisie consiste à enchaîner les éléments suivants protected horizontalement et copier les membres du prototype du type déclarant. Cela rend possible l'imbrication des classes, car les membres déclarés sur un type moins dérivé peuvent être propagés aux types plus dérivés ; la fonction transmit dans mon code. Si A , D y G ont leurs propres membres protégés, cela ressemblerait à ce qui suit :

    bhcsI.png

    La fermeture permettant d'accéder à l'instance privée est la suivante this[''] . Il prend un argument qui sert à identifier une classe. Le support des modificateurs est simplement l'identifiant de la classe, nommé y en Function.extend y _ dans le code de test, elle ne doit pas être exposée en dehors de la déclaration de la classe. Il est également utilisé comme raccourci de this[''] .

    _['base'] est en fait non seulement l'invocateur du constructeur de base, mais aussi le créateur des instances privées. Il crée les instances privées et met à jour this[''] pour chaque constructeur avec l'héritage, de sorte qu'il doit toujours être appelé dans les constructeurs.

    Bien qu'une instance privée ait accès aux membres publics, elle ne doit pas être utilisée pour les modifier, puisque this[''] n'est pas garantie d'être invoquée lors de l'accès aux membres publics. En revanche, l'accès aux instances privées l'est ; recent se souvient de l'instance privée la plus récemment consultée et met à jour les membres protégés en cas de changement.

    Ma question est la suivante : comment puis-je me débarrasser de ce type de rafraîchissement des membres protégés ? Existe-t-il de meilleures idées pour parvenir à une encapsulation plus réaliste ?

    p.s. : En fait, je ne veux pas d'une solution qui utilise des méthodes/propétences non standard et il serait préférable qu'il y ait des polyfills si les méthodes/propétences utilisées sont trop à la mode pour les anciens navigateurs.


  • Function.extend

    Function.extend=function(base, factory) {
        factory.call(initializeClass);
        updateStaticMembersOfDerivedInnerClasses(y['public'].constructor);
        transfer(y['protected'], y['public']);
        return y['public'].constructor;
    
        function y($this) {
            return $this[''](y);
        }
    
        function transfer(target, source, descriptor) {
            if(target!==source?
                'undefined'!==typeof target?
                    'undefined'!==typeof source:
                        false:false) {
                var keys='undefined'!==typeof descriptor? descriptor:source;
    
                for(var key in keys) {
                    if(Object.prototype.hasOwnProperty.call(source, key)) {
                        target[key]=source[key];
                    }
                }
            }
        }
    
        function updateStaticMembersOfDerivedInnerClasses(outer) {
            var member, inner;
    
            for(var key in outer) {
                if(Object.prototype.hasOwnProperty.call(outer, key)?
                    (member=outer[key]) instanceof outer?
                        outer!==(inner=member.constructor):
                            false:false) {
                    transfer(inner, outer);
                }
            }
        }
    
        function initializeInstance() {
            var $this=Object.create(y['private']);
            var derivedGet=this[''];
            var recent=$this;
    
            this['']=function(x) {
                var value=y!==x? derivedGet.call(this, x):$this;
    
                if(value!==recent) {
                    transfer(value, recent, x['protected']);
                    recent=value;
                }
    
                transfer(value, this);
                return value;
            };
    
            base.apply(this, arguments);
            $this['']=this[''];
        }
    
        function initializeClass(derived) {
            y['public']=Object.create(base.prototype);
            y['public'].constructor=derived;
    
            if(Object.prototype.hasOwnProperty.call(base, 'transmit')) {
                base.transmit(y);
            }
            else {
                y['protected']=Object.create(y['public']);
            }
    
            y['private']=Object.create(y['protected']);
            y['base']=initializeInstance;
            transfer(derived, base);
    
            derived.transmit=function(x) {
                if(x['public'] instanceof derived) {
                    x['protected']=Object.create(y['protected']);
                    x['protected'].constructor=x['public'].constructor;
                }
            };
    
            derived.prototype=y['public'];
            return y;
        }
    };
  • code de test

    'use strict';
    
    var BaseClass=Function.extend(Object, function () {
        var _=this(BaseClass);
    
        var NestedClass=Function.extend(BaseClass, function () {
            var _=this(NestedClass);
    
            function NestedClass(x, y, z) {
                _['base'].apply(this, arguments);
                _(this).Y=y;
                _(this).Z=z;
            }
    
            _['public'].SetX=function (x) {
                _(this).InternalSetX(x);
            };
    
            _['public'].GetX=function () {
                return _(this).InternalGetX();
            };
    
            _['public'].GetY=function () {
                return _(this).Y;
            };
    
            _['public'].SetZ=function (z) {
                _(this).Z=z;
            };
    
            _['public'].GetZ=function () {
                return _(this).Z;
            };
    
            _['private'].Y=0;
        });
    
        function BaseClass(x) {
            _['base'].apply(this, arguments);
            _(this).X=x;
        }
    
        _['protected'].InternalSetX=function (x) {
            _(this).X=x;
        };
    
        _['protected'].InternalGetX=function () {
            return _(this).X;
        };
    
        _['private'].X=0;
        _['protected'].Z=0;
    
        BaseClass.Sample=new NestedClass(1, 2, 3);
    });
    
    var DerivedClass=Function.extend(BaseClass, function () {
        var _=this(DerivedClass);
    
        function DerivedClass(x, y, z) {
            _['base'].apply(this, arguments);
        }
    });
    
    var o=DerivedClass.Sample;
    alert(o.GetX());
    alert(o.GetY());
    alert(o.GetZ());
    o.SetX(3);
    o.SetZ(1);
    alert(o.GetX());
    alert(o.GetY());
    alert(o.GetZ());

0 votes

Avez-vous envisagé un compilateur croisé comme script# à la place ? Considérez également si la prise en charge de protected Cela vaut vraiment la peine - "par convention" est généralement suffisant pour les équipes amicales, privé/public strict est préférable pour les utilisateurs externes ( parashift.com/c++-faq-lite/protected-data-not-evil.html )...

10 votes

Il semble que TypeScript supporte les modificateurs d'accès, si cela vous intéresse, bien que ce ne soit pas protégé.

3 votes

Non, il s'agit plutôt d'écrire du code fortement typé tout en étant capable de l'utiliser avec un navigateur - C# -> JS peut être la voie la plus pratique. Note complémentaire : jusqu'à présent, votre belle et longue question semble manquer de valeur pratique, de sorte que certains pourraient la considérer comme hors sujet pour l'OS. En tout cas, un résumé de 2 ou 3 phrases au début pourrait être plus efficace que les cas TL;DR...

4voto

Matthew.Lothian Points 960

J'ai également eu une pensée similaire et j'ai décidé d'essayer d'écrire quelque chose. Une solution js vanille. C'est encore un peu tôt, mais j'aime ce qui en est ressorti. Vous pourriez le trouver intéressant aussi.

Ce n'est pas exactement le c#, mais il offre un écosystème plus strict. Et d'autres fonctionnalités avancées de js dans une solution légère.

https://github.com/iamlothian/rucksack.js

Il ne s'agit pas d'une solution à votre code, mais d'une solution à votre concept. Si votre but était de faire fonctionner votre idée, alors continuez car je suis intéressé par le résultat.

Si, comme moi, vous souhaitez simplement un environnement js plus structuré, en voici un que j'ai écrit avec une ambition similaire aux concepts de vos questions.

Partie 2 :

L'idée est ici d'utiliser la fermeture et la restriction d'accès pour créer un modèle qui limite la façon dont le code peut être utilisé et modifié après avoir été défini. Pour l'essentiel, le plus gros du travail a été fait. Mais c'est à vous de définir le modèle.

Voici un exemple rapide qui montre comment vous pouvez mettre en œuvre un héritage public|protégé|privé. J'essaie de décider si j'implémente certaines de ces fonctions comme une fonctionnalité intégrée ou si je laisse les utilisateurs implémenter leur propre extension d'objet comme je l'ai fait dans l'exemple.

http://plnkr.co/edit/ao2hTyBV1b3nYIwr7ZS5

L'implémentation se trouve dans scripts.js. Affichez votre console pour voir ce qui se passe.

Le sac à dos fournit un cadre pour la création de modules de code détachés. Ces modules sont regroupés dans des espaces de noms et peuvent dépendre les uns des autres. Ces dépendances sont résolues paresseusement au fur et à mesure qu'elles sont définies, de sorte que l'ordre de définition n'est pas vraiment important. Le processus de résolution offre d'autres fonctionnalités utiles telles que la correspondance des interfaces et le module scellé.

caractéristiques actuelles :

  • Modulaire
  • Injection de dépendance
  • Constructeur d'usine (Instances Object)
  • Constructeur de service (objets statiques)
  • Chargement paresseux
  • Enregistrement facile des erreurs (toutes les erreurs au sein des modules sont enregistrées et peuvent être transmises)
  • Espaces de noms
  • Modules et espaces de noms scellables (modules inaccessibles depuis l'extérieur de l'espace de noms)
  • Événement d'attente global pour le module
  • Interface pour l'objet de configuration optionnel
  • Contrôles d'interface stricts facultatifs pour l'injection

0 votes

Il fait plus que ce que vous avez suggéré, il vise à donner aux développeurs plus de contrôle sur la façon dont leur code est utilisé et distribué. Tout en offrant des conséquences similaires à celles d'un langage plus strictement typé.

1 votes

Comme c'est assez long, je suis vraiment désolé de ne pas prendre la peine de lire la mise en œuvre complète peut-être allez-vous extraire la partie pertinente pour cette question ?

1 votes

Comme je suis entre deux vols sur mon portable, cela devra attendre. J'ai mis à jour ma réponse avec une courte explication.

3voto

Tengiz Points 2800

Bien que le code avec fermeture puisse résoudre ce que vous voulez, j'opterais pour la solution plus simple suivante Les méthodes privilégiées, telles que Crockford les a appelées ici .

L'idée d'utilisation est simple :

  • Définir une méthode privilégiée sur l'objet de base (avec une limite 1 - ne peut être appelée qu'une seule fois).
  • Une méthode privilégiée renvoie une interface protégée d'elle-même (d'un objet de base) qui contient des fonctions protégées (ces fonctions sont probablement définies de manière privée dans l'objet de base, puis copiées dans l'objet d'interface protégé... ou peut-être que l'interface protégée existe de manière privée).
  • Chaque objet étend son interface protégée avec l'interface protégée de son objet de base et l'expose toujours à travers la méthode privilégiée.

Vous obtiendrez quelque chose comme ceci :

function A() {
    var protected = {
        protectedA: function() { }
    };

    this.getProtected = (function() {
        var allow = true;

        //privileged function.
        return function() {
            if (allow) {
                allow = false;
                return protected;
            }
        };
    });
}

//B - derives from (extends) A
function B() {
    var base = {}; //acquiring a base omitted - depends on your implementation.

    var protected = {
        protectedB: function() { }
    };
    //"extend" simply copies new members into protected:
    protected = $.extend(protected, base.getProtected()); 

    this.getProtected = function() { 
        /* privileged function - similar to A.getProtected */ 
    };
}

JavaScript a des capacités limitées dans ce domaine. protected Le sucre a de toute façon un coût.

0 votes

Merci de répondre. Mais je ne comprends pas encore très bien la réponse. extend et comment puis-je créer un modèle réutilisable à partir de cette méthode ?

0 votes

Extend ne fait que copier les propriétés - tout comme jQuery.extend. J'ai fait une modification pour l'indiquer. Cependant, si vous n'utilisez pas jQuery, vous devrez l'imiter. En ce qui concerne la réutilisation : votre Function.extend doit s'assurer que chaque objet possède la méthode getProtected. Et le type de base de vos classes peut retourner simplement un objet vide {} comme conteneur des membres protégés.

0 votes

Je pense que cela permet de moins simuler un langage basé sur les types.

2voto

Paulo Roberto Points 634

Javascript est un langage très vaste, car il permet de faire presque tout ce que l'on veut dans une page web, simplement en créant des fonctions et en trouvant des moyens de le faire.

Je peux vous dire que JavaScript n'est pas un langage sûr, car vous pouvez facilement accéder à la plupart des variables et des fonctions, les lire et savoir comment elles fonctionnent, simplement en accédant au fichier .js, inclus dans la page.

Ma pensée : Certains modificateurs d'accès n'ont pas été créés pour être utilisés en javascript, car les développeurs savent déjà qu'ils peuvent être inutiles, car JavaScript ne peut pas "voyager" dans d'autres endroits (pages), à moins d'utiliser une variable de session.

Et à propos des modificateurs :

  • Privé

  • Protégé

  • Public

Je peux vous dire que je connais quelques modificateurs javascript qui ont une certaine similitude avec eux, qui sont :

Localement :

var Variable = 0;

Automatiquement, cette variable est convertie en une variable de type Integer, parce qu'elle reçoit une valeur Integer, et aussi, c'est une variable LOCAL à cause de l'attribut var qui déclarent cette variable de manière à ce que vous ne puissiez pas accéder à sa valeur, sauf si vous vous trouvez dans la même fonction que celle dans laquelle cette variable a été déclarée.

Exemple :

Si vous déclarez ces fonctions de cette manière, avec des modificateurs par défaut :

function conflict(){
  i = 2;
  changeI();
  alert(i);
}
function changeI(){
  i = 0;
}

Dans ce cas, le i est la même variable pour les deux fonctions.

Ainsi, si vous exécutez conflict(); vous obtiendrez un message d'alerte résultant 0 .

MAIS, si vous déclarez i en utilisant le var modificateur :

function conflict(){
  var i = 2;
  changeI();
  alert(i);
}
function changeI(){
  var i = 0;
}

Dans ce cas, vous avez deux i parce qu'elles ne peuvent être utilisées qu'à l'intérieur de leur fonction, donc si vous exécutez conflict(); vous obtiendrez une alerte avec la valeur de 2 .

Variable de classe :

this.Variable = "a";

Cette variable est automatiquement une chaîne de caractères, car elle reçoit une valeur de type chaîne de caractères. this mais, je vais essayer d'expliquer avec mon point de vue, c'est-à-dire que cette variable provient de la SuperClasse ou en javascript une "SuperFonction" que l'on peut appeler une Classe, ou en d'autres termes, la classe "père".

Un exemple :

function TClass()
{
  this.getVar = function()
  {
    try
    {
      return "test";
    }
    catch(err)
    {
      return false;
    }
  }
  this.alertVar = function()
  {
    try
    {
      alert(this.getVar());
    }
    catch(err)
    {
      alert('error');
    }
  }
}
var $Class = new TClass();

Comme vous le voyez ci-dessus, j'ai créé une classe TClass et quelques variables contenant des fonctions (fermeture javascript) et a ajouté le modificateur this. à eux, pour les rendre liés à la TClass et comme vous le voyez sur le alertVar() j'accède à la fonction alert(this.getVar()); la fonction qui provient de la TClass qui est égal à this dans ce contexte.

Et cette partie : var $Class = new TClass(); Je crée la classe, comme vous le savez probablement, pour avoir accès à ses méthodes, ce qui me permet de l'exécuter, de la tester :

$Class.alertVar();

Et nous obtenons comme résultat une boîte d'alerte contenant "test", comme vous pouvez le voir :

the test alert

Notez que vous ne pouvez pas accéder au TClass d'une autre manière, vous ne pouvez y accéder qu'en créant la classe et en y accédant par elle.

J'espère donc que vous avez compris la facilité d'utilisation de l'application this modificateur.

Global :

window.Variable = true;

Javascript déclare automatiquement que cette variable est booléenne, car elle reçoit une valeur booléenne. La variable window comme il est dit, vous pouvez y accéder où que vous soyez dans la fenêtre où vous vous trouvez, parce que les variables javascript, lorsqu'elles sont déclarées, vont dans le fichier DOM dans la fenêtre, voir ce qu'est DOM :

DOM (Document Object Model) : Le DOM est un système multiplateforme qui représente la manière dont les balises html, xhtml et xml sont organisées et lues par le navigateur que vous utilisez. En d'autres termes, si vous accédez au DOM, vous pouvez voir chaque propriété, chaque variable ou autre chose qui existe dans le navigateur à ce moment-là.

Différente des autres variables, la window Les variables peuvent se voir assigner une autre valeur et accéder à la valeur réelle, où que vous soyez, à l'intérieur d'une fonction ou non, à l'intérieur d'un fichier js ou non.

Exemple de Global(window) :

Exécuter sur le onLoad d'une page, un code qui déclare un événement window ou la déclarer vous-même en utilisant la console du navigateur :

window.Variable = true;

Ensuite, ajoutez un fichier JS contenant cette fonction, ou créez-la vous-même en exécutant le code dans la console du navigateur :

function testGlobalVar(){
  if (Variable)
    alert("it works!");
}

Lorsque vous exécutez testGlobalVar() vous obtiendrez l'alerte, mais c'est uniquement parce que vous l'avez déclarée comme `window', sinon vous n'obtiendrez rien.

Modificateur par défaut :

Variable = 0.5

Automatiquement, cette variable est déclarée comme flottante parce qu'elle reçoit une valeur flottante. Je ne sais pas si vous le savez déjà, mais les variables javascript déclarées de la manière habituelle, ont un modificateur par défaut qui rend la variable similaire à window Les variables, mais vous ne pouvez pas y accéder où que vous soyez, mais dans la plupart des cas vous pouvez y accéder, en particulier, je ne connais pas tous les cas où vous ne pouvez pas y accéder, mais je sais que vous ne pouvez pas lorsque vous chargez un fichier js et qu'il a été déclaré à l'intérieur de celui-ci. Seulement si vous exécutez une fonction qui le déclare, et qu'après vous essayez d'y accéder.

Au fait, je vois que vous voulez connaître les modificateurs qui correspondent aux trois que vous avez mentionnés, mais à mon avis, certains de ces modificateurs que je vous ai indiqués peuvent être utilisés pour faire la même chose que vos modificateurs en c#.

J'espère que vous comprenez ce que je dis.

Ah, et si vous étiez confus lorsque vous avez vu une fonction à l'intérieur d'une variable, étudiez les closures Javascript, vous comprendrez après ça :).

0 votes

Pourquoi ? Vous avez demandé : comment implémenter des modificateurs d'accès c# en javascript, et je vous ai dit tous les modificateurs d'accès existants en javascript, donc huh

1 votes

Si vous ne parvenez pas à résoudre votre question en utilisant les méthodes que j'ai expliquées en détail ci-dessus, abandonnez. Parce qu'il n'y a pas d'autre moyen, car javascript est un langage à portée imbriquée.

0 votes

Oui, je pense que vous avez expliqué en détail. Mais pour autant que je sache, javascript n'a pas de modificateur d'accès équivalent à protégé dans les langages à base de types. Ce que j'essaie de faire est plus que cela, comme je l'ai décrit dans le contenu plus détaillé que le titre, bien que

1voto

Blake Regalia Points 554

Comment les classes de parents et d'enfants interagissent l'une avec l'autre

  • Une classe enfantine qui s'étend appelle super.call une fonction qui construit une instance de son parent.

  • Une classe mère partage ses membres protégés (champs et fonctions) avec sa sous-classe d'extension à l'aide de la fonction this.share dans son constructeur.

  • Une sous-classe peut également appeler super.fetch() qui renvoie l'objet des champs/fonctions que la classe mère a transmis à this.share


Pour illustrer ma technique, le code suivant démontre quelques éléments essentiels de la POO avec un exemple simple d'un class Dog extends Animal

Quelques fonctions essentielles de ce modèle orienté objet

// runs in both node.js and browser
var global_namespace = ('undefined'==typeof module)? window: global;

// put a no-operation function in the value for `share` in case nothing is extending a class
var not_extendable = {share:function(){}};

// when something is extending a class...
var extendable = function(constructor) {

    // create a space for protected fields
    var protected_space = {};

    // the following is what will get passed as `this` to the parent constructor
    var sharing = {
        share: function(fields) {
            protected_space = fields;
        },
    };

    // the following is what will get passed as the first arg to the child constructor
    return {

        // enables child to call its parent's constructor
        call: function(args) {
            return constructor.apply(sharing, args);
        },

        // allows child to access protected fields shared by its parent
        fetch: function() {
            return protected_space;
        },
    };
};

Animal clase

// class definition for `Animal`
(function(namespace) {

    // construct an instance of this class
    var constructor = function(name, weight, info) {

        // private fields
        var color = (info && info.color) || 'unknown';

        // for protected fields
        var protect = {
            weight: weight,
            noise: function() {
                return 'nothing';
            },
        };

        // share the protected fields with any subclass that might be extending this
        this.share(protect);

        // public fields and methods
        return {
            speak: function() {
                console.log(name+' says '+protect.noise());
            },
            feed: function() {
                console.log(name+' is not hungry');
            },
            weigh: function() {
                console.log(name+' weighs '+protect.weight+' lbs');
            },
            toString: function() {
                return '{Animal}';
            },
        };
    };

    // handle calls to: `Animal()`
    namespace.Animal = function() {

        // called with new operator: `new Animal(...)`
        if(this !== namespace) {

            // construct simple instance of this class
            return constructor.apply(not_extendable, arguments);
        }

        // static call: `Animal(...)`, means the caller wants to extend this class
        else {

            // reference child constructor
            var child_constructor = arguments[0];

            // return a wrapped constructor function
            return function() {

                // call child constructor and allow it to call the super constructor
                return child_constructor.apply({}, [extendable(constructor), arguments]);
            };
        }
    };

})(global_namespace);

Dog clase

// class definition for `Dog`
(function(namespace) {

    // class `Dog` extends class `Animal`
    var constructor = Animal(function(super_class, args) {

        // private field
        var been_fed = false;

        // call super's constructor
        var operator = super_class.call(args);

        // inherit parent's protected members
        var parent = super_class.fetch();

        // override a protected method
        parent.noise = function() {
            return 'bark!';
        };

        // override a public method
        operator.feed = function() {
            been_fed = true;
            parent.weight += 5;
        };

        // extend a public method
        var super_weigh = operator.weigh;
        operator.weigh = function() {
            super_weigh();
            if(been_fed) console.log('\t'+args[0]+' has been eating :)');
            else console.log('\t'+args[0]+' has not been fed yet');
        };

        // override another public method
        operator.toString = function() {
            return '{Dog}';
        },

        // return the operator (interfacable instance object)
        return operator;
    });

    // handle calls to: `Dog()`
    namespace.Dog = function() {

        // called with new operator: `new Dog()`
        if(this !== namespace) {
            return constructor.apply(this, arguments);
        }

        // static call: `Dog()`
        else {

            // we do no allow extending class `Dog`
            return false;
        }
    };

})(global_namespace);

Maintenant, nous pouvons le faire :

var giraffe = new Animal('Mr. Giraffe', 720);
giraffe.speak(); // "Mr. Giraffe says nothing"
giraffe.weigh(); // "Mr. Giraffe weighs 720 lbs"

var buddy = new Dog('Buddy', 50);
buddy.speak(); // "Buddy says bark!"
buddy.weigh(); // "Buddy weighs 50 lbs"
                   //   "Buddy has not been fed yet"
buddy.feed();
buddy.weigh(); // "Buddy weighs 55 lbs"
                   //   "Buddy has been eating :)"

Cela permet d'avoir des champs/fonctions privés, protégés et publics. Les champs/fonctions protégés et publics peuvent être écrasés et étendus.

console.log(giraffe); // "{Animal}"
console.log(buddy);   // "{Dog}"

1voto

Matthew.Lothian Points 960

J'ai travaillé sur un autre projet javascript intéressant et j'ai implémenté quelque chose qui pourrait être plus proche de ce que vous recherchez.

Implement.js

Vos réflexions m'intéressent.

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