Comment déclarer un espace de noms en JavaScript?

990

Comment créer un espace de noms en JavaScript afin que mes objets et fonctions ne soient pas remplacés par d'autres objets et fonctions portant le même nom? J'ai utilisé ce qui suit:

if (Foo == null || typeof(Foo) != "object") { var Foo = new Object();}

Existe-t-il une manière plus élégante ou succincte de procéder?

Scott McKenzie
la source
20
Je peux voir où vous allez avec la vérification pour voir si l'espace de noms est pris, mais puisque l'objet ne sera pas créé si cela échoue, je pense que la meilleure approche est d'alerter si l'espace de noms est pris. Franchement, cela ne devrait tout simplement pas se produire dans la plupart des situations JS et devrait être rapidement détecté dans le développement.
annakata
18
Prenez un "espace de noms" de niveau supérieur (propriété window). Posséder. Les conflits doivent être détectés dès le début des tests. Ne vous embêtez pas à ajouter toutes ces vérifications «et si». C'est un problème fatal pour les "espaces de noms" en double et doit être traité comme tel . Vous pouvez suivre une approche comme jQuery pour permettre d'habiter un "espace de noms" personnalisé; mais c'est toujours un problème de conception.
Veuillez modifier votre réponse acceptée sur stackoverflow.com/questions/881515/… , qui est une solution beaucoup plus élégante et mise à jour.
hlfcoding
@pst qu'en est-il de ce que fait YUI? Je pense qu'ils font exactement cela pour ajouter progressivement à leur espace de noms. des astuces comme celle-ci sont sûrement nécessaires pour des performances dans un environnement HTTP?
Simon_Weaver
voir aussi stackoverflow.com/questions/2102591/… pour les problèmes de performances
Tim Abell

Réponses:

764

J'aime ça:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();
dfa
la source
62
L'important est d'être religieux pour ne pas développer plus que la variable racine. Tout doit en découler.
annakata
22
Cela ne crée pas de fermeture pour votre code - cela rend fastidieux d'appeler vos autres fonctions car elles doivent toujours ressembler à: yourNamespace.bar (); J'ai créé un projet open source JUST pour résoudre ce problème de conception: github.com/mckoss/namespace .
mckoss
24
annakata: "Le point important est d'être religieux pour ne pas développer plus que la variable racine." - Pourquoi est-ce?
user406905
11
@alex - pourquoi devrait-il y avoir une structure d'objet peu profonde?
Ryan
25
@Ryan, je voulais dire que tout devrait être sous MyApp, par exemple MyApp.Views.Profile = {}plutôt que MyApp.users = {}et MyViews.Profile = {}. Pas nécessairement qu'il ne devrait y avoir que deux niveaux de profondeur.
alex
1042

J'utilise l'approche trouvée sur le site Enterprise jQuery :

Voici leur exemple montrant comment déclarer des propriétés et fonctions privées et publiques. Tout se fait comme une fonction anonyme auto-exécutable.

(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;

    //Public Property
    skillet.ingredient = "Bacon Strips";

    //Public Method
    skillet.fry = function() {
        var oliveOil;

        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };

    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }
}( window.skillet = window.skillet || {}, jQuery ));

Donc, si vous voulez accéder à l'un des membres du public, vous iriez skillet.fry()ou skillet.ingredients.

Ce qui est vraiment cool, c'est que vous pouvez maintenant étendre l'espace de noms en utilisant exactement la même syntaxe.

//Adding new Functionality to the skillet
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";

    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " +
                     skillet.ingredient + " & " +
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
    };
}( window.skillet = window.skillet || {}, jQuery ));

Le troisième undefinedargument

Le troisième undefinedargument, est la source de la variable de valeur undefined. Je ne sais pas si elle est toujours pertinente aujourd'hui, mais en travaillant avec des navigateurs / normes JavaScript plus anciens (ecmascript 5, javascript <1.8.5 ~ firefox 4), la variable de portée globale undefinedest accessible en écriture, donc tout le monde peut réécrire sa valeur. Le troisième argument (lorsqu'il ne undefinedreçoit pas de valeur) crée une variable nommée qui est étendue à l'espace de noms / fonction. Étant donné qu'aucune valeur n'a été transmise lorsque vous avez créé l'espace de noms, il s'agit par défaut de la valeur undefined.

Jaco Pretorius
la source
9
+1 pour cet excellent échantillon. Pour toute personne intéressée, cet échantillon faisait partie de l'excellente présentation d'Elijah Manor au Mix 2011 (ignorez le titre) live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
Darren Lewis
11
Dans l'article d'Elie, voici les avantages et les inconvénients de cette approche, paraphrasés. Pour: 1. Propriétés et méthodes publiques et privées, 2. n'utilise pas d'OLN encombrant, 3. Protège undefined 4. Garantit que $ fait référence à jQuery, 5. L'espace de noms peut s'étendre sur des fichiers, Contre: Plus difficile à comprendre que OLN
Jared Beck
4
C'est ce qu'on appelle aujourd'hui IIFE ( Immediateely Invoked Function Expression ). Merci pour votre réponse +1!
Gustavo Gondim
20
@CpILL: je ne sais pas s'il est toujours pertinent, mais le troisième undefinedargument est la source de la variable de valeur undefined. En travaillant avec des navigateurs plus anciens / standard javascript (ecmascript 5, javascript <1.8.5 ~ firefox 4), la variable de portée globale undefinedest accessible en écriture, donc tout le monde peut réécrire sa valeur. L'ajout d'un troisième argument supplémentaire que vous ne transmettez pas en fait une valeur undefined, vous créez donc une étendue d'espace de noms undefinedqui ne sera pas réécrite par des sources externes.
mrówa
4
@SapphireSun L'avantage window.skillet = window.skillet || {}est qu'il permet à plusieurs scripts d'ajouter en toute sécurité au même espace de noms lorsqu'ils ne savent pas à l'avance dans quel ordre ils s'exécuteront. Cela peut être utile si vous souhaitez pouvoir réorganiser arbitrairement vos inclusions de script sans casser votre code, ou si vous souhaitez charger des scripts de manière asynchrone avec l'attribut async et que vous n'avez donc aucune garantie sur l'ordre d'exécution. Voir stackoverflow.com/questions/6439579/…
Mark Amery
338

Une autre façon de le faire, que je considère comme un peu moins restrictive que la forme littérale de l'objet, est la suivante:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

Ce qui précède est à peu près comme le modèle de module et que cela vous plaise ou non , il vous permet d'exposer toutes vos fonctions en public, tout en évitant la structure rigide d'un objet littéral.

Ionuț G. Stan
la source
16
1. Il y a une différence entre OLN et le modèle de module. 2. Je n'aime pas / toujours / OLN car vous devez vous rappeler de ne pas mettre la dernière virgule de fin et tous vos attributs doivent être initialisés avec une valeur (comme null ou indéfini). De plus, si vous avez besoin de fermetures pour les fonctions membres, vous aurez besoin de petites usines de fonctions pour chacune de ces méthodes. Une autre chose est que vous devez enfermer toutes vos structures de contrôle dans des fonctions, alors que la forme ci-dessus ne l'impose pas. Cela ne veut pas dire que je n'utilise pas OLN, c'est juste que parfois je ne l'aime pas.
Ionuț G. Stan
8
J'aime cette approche car elle permet des fonctions privées, des variables et des pseudo-constantes (ie var API_KEY = 12345;).
Lawrence Barsanti du
12
J'aime mieux que le conteneur d'objets séparés par des virgules qui a voté plus haut. Je ne vois pas non plus de lacunes en comparaison. Suis-je en train de manquer quelque chose?
Lucent
7
JS Newbie ici ... pourquoi est-ce que je n'ai pas à taper ns().publicFunction(), c'est ... ça ns.publicFunction()marche.
John Kraft
14
@John Kraft, c'est à cause du newmot - clé devant le functionmot - clé. Fondamentalement, ce qui se passe, c'est qu'il déclare une fonction anonyme (et en tant que fonction, il est également un constructeur), puis il l'invoque immédiatement en tant que constructeur en utilisant new. En tant que tel, la valeur finale qui est stockée à l'intérieur nsest une instance (unique) de ce constructeur anonyme. J'espère que cela a du sens.
Ionuț G. Stan
157

Existe-t-il une manière plus élégante ou succincte de procéder?

Oui. Par exemple:

var your_namespace = your_namespace || {};

alors vous pouvez avoir

var your_namespace = your_namespace || {};
your_namespace.Foo = {toAlert:'test'};
your_namespace.Bar = function(arg) 
{
    alert(arg);
};
with(your_namespace)
{
   Bar(Foo.toAlert);
}
Alex Pacurar
la source
1
cela me donne une erreur dans IE7. var your_namespace = (typeof your_namespace == "undefined" ||! your_namespace)? {}: your_namespace; fonctionne mieux.
mjallday
9
ce devrait être var your_namespace = your_namespace = your_namespace || {} Fonctionne dans tous les navigateurs;)
Palo
+1 de moi! Thin one fonctionne comme Jaco Pretorius répond en étendant une bibliothèque à différents fichiers ou à différents endroits dans le même fichier. Tout simplement génial!
Centurian
2
@Palo Pouvez-vous expliquer pourquoi il devrait en être ainsi? var your_namespace = your_namespace = your_namespace || {}
Sriram
6
vous auriez la possibilité d'étendre l'objet your_namespace dans différents fichiers js. Lorsque vous utilisez var your_namespace = {}, vous ne pouvez pas le faire, car l'objet sera remplacé par chaque fichier
Alex Pacurar
93

Je le construis normalement dans une fermeture:

var MYNS = MYNS || {};

MYNS.subns = (function() {

    function privateMethod() {
        // Do private stuff, or build internal.
        return "Message";
    }

    return {
        someProperty: 'prop value',
        publicMethod: function() {
            return privateMethod() + " stuff";
        }
    };
})();

Mon style au fil des ans a changé subtilement depuis que j'écris ceci, et je me retrouve maintenant à écrire la fermeture comme ceci:

var MYNS = MYNS || {};

MYNS.subns = (function() {
    var internalState = "Message";

    var privateMethod = function() {
        // Do private stuff, or build internal.
        return internalState;
    };
    var publicMethod = function() {
        return privateMethod() + " stuff";
    };

    return {
        someProperty: 'prop value',
        publicMethod: publicMethod
    };
})();

De cette façon, je trouve l'API publique et l'implémentation plus faciles à comprendre. Considérez la déclaration de retour comme une interface publique pour l'implémentation.

Brett Ryan
la source
3
Devez-vous pas vérifier MYNS.subns = MYNS.subns || {}??
Mirko
Un bon point qui devrait être l'exercice à l'intention des développeurs. Vous devez considérer ce qu'il faut faire quand il existe, le remplacer, l'erreur, utiliser la vérification existante ou la version et remplacer conditionnellement. J'ai eu des situations différentes qui appellent pour chaque variante. Dans la plupart des cas, vous avez peut-être cela comme un cas de bord à faible risque et le remplacement peut être bénéfique, envisagez un module escroc qui a tenté de détourner le NS.
Brett Ryan
1
Il y a une explication de cette approche dans le livre "Speaking Javascript" à la page 412 si quelqu'un l'a, sous le titre "Modules rapides et sales".
Soferio
2
Conseil d'optimisation: tout var foo = functionet function foosont similaires, étant privé; en raison de la nature dynamiquement typée de JavaScript, ce dernier est légèrement plus rapide car il saute quelques instructions dans la plupart des pipelines d'interprètes. Avec var foo, le système de type doit être invoqué pour savoir quel type est affecté à ladite var, tandis qu'avec function foo, le système de type sait automatiquement que c'est une fonction, donc quelques appels de fonction sont ignorés, ce qui se traduit par moins d'appels d'instructions de processeur comme jmp, pushq, popq, etc, ce qui se traduit par un pipeline de processeur plus courte.
Braden Best
1
@brett oops. Tu as raison. Je pensais à un langage de script différent. Bien que j'insiste toujours sur le fait que la function foosyntaxe est plus lisible. Et j'aime toujours ma version.
Braden Best
56

Parce que vous pouvez écrire différents fichiers JavaScript et les combiner ultérieurement ou non dans une application, chacun doit pouvoir récupérer ou construire l'objet d'espace de noms sans endommager le travail des autres fichiers ...

Un fichier peut avoir l'intention d'utiliser l'espace de noms namespace.namespace1:

namespace = window.namespace || {};
namespace.namespace1 = namespace.namespace1 || {};

namespace.namespace1.doSomeThing = function(){}

Un autre fichier peut vouloir utiliser l'espace de noms namespace.namespace2:

namespace = window.namespace || {};
namespace.namespace2 = namespace.namespace2 || {};

namespace.namespace2.doSomeThing = function(){}

Ces deux fichiers peuvent vivre ensemble ou séparément sans entrer en collision.

Fentex
la source
1
J'ai trouvé que c'était une méthode très utile pour organiser le script client en plusieurs fichiers dans de grandes applications où la fonctionnalité doit être modulaire.
DVK
Question demandant spécifiquement plusieurs fichiers: stackoverflow.com/questions/5150124/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
49

Voici comment Stoyan Stefanov le fait dans son livre sur les modèles JavaScript que j'ai trouvé très bon (il montre également comment il fait des commentaires qui permettent une documentation API générée automatiquement, et comment ajouter une méthode au prototype d'un objet personnalisé):

/**
* My JavaScript application
*
* @module myapp
*/

/** @namespace Namespace for MYAPP classes and functions. */
var MYAPP = MYAPP || {};

/**
* A maths utility
* @namespace MYAPP
* @class math_stuff
*/
MYAPP.math_stuff = {

    /**
    * Sums two numbers
    *
    * @method sum
    * @param {Number} a First number
    * @param {Number} b Second number
    * @return {Number} Sum of the inputs
    */
    sum: function (a, b) {
        return a + b;
    },

    /**
    * Multiplies two numbers
    *
    * @method multi
    * @param {Number} a First number
    * @param {Number} b Second number
    * @return {Number} The inputs multiplied
    */
    multi: function (a, b) {
        return a * b;
    }
};

/**
* Constructs Person objects
* @class Person
* @constructor
* @namespace MYAPP
* @param {String} First name
* @param {String} Last name
*/
MYAPP.Person = function (first, last) {

    /**
    * First name of the Person
    * @property first_name
    * @type String
    */
    this.first_name = first;

    /**
    * Last name of the Person
    * @property last_name
    * @type String
    */
    this.last_name = last;
};

/**
* Return Person's full name
*
* @method getName
* @return {String} First name + last name
*/
MYAPP.Person.prototype.getName = function () {
    return this.first_name + ' ' + this.last_name;
};
Ciaran Bruen
la source
32

J'utilise cette approche:

var myNamespace = {}
myNamespace._construct = function()
{
    var staticVariable = "This is available to all functions created here"

    function MyClass()
    {
       // Depending on the class, we may build all the classes here
       this.publicMethod = function()
       {
          //Do stuff
       }
    }

    // Alternatively, we may use a prototype.
    MyClass.prototype.altPublicMethod = function()
    {
        //Do stuff
    }

    function privateStuff()
    {
    }

    function publicStuff()
    {
       // Code that may call other public and private functions
    }

    // List of things to place publically
    this.publicStuff = publicStuff
    this.MyClass = MyClass
}
myNamespace._construct()

// The following may or may not be in another file
myNamespace.subName = {}
myNamespace.subName._construct = function()
{
   // Build namespace
}
myNamespace.subName._construct()

Le code externe peut alors être:

var myClass = new myNamespace.MyClass();
var myOtherClass = new myNamepace.subName.SomeOtherClass();
myNamespace.subName.publicOtherStuff(someParameter);
AnthonyWJones
la source
Grand détail! Merci! Je me demande simplement ce que vous pensez de Namespace.js. Je ne l'ai jamais utilisé moi-même, donc je me demande si quelqu'un avec vos connaissances / compétences / expérience envisagerait de l'utiliser.
John
Je l'aime! D'un autre côté, j'obtiens une exception sur la première ligne de ce code externe, en disant: 'myNameSpace.MyClass' [undefined] n'est pas un constructeur. cela dépend peut-être de la mise en œuvre de JS? : /
yoosiba
@yossiba: Peut-être. Le code ci-dessus est assez standard. Dans JS standard, n'importe quelle fonction peut être utilisée comme constructeur, il n'y a rien à faire pour marquer une fonction comme spécifiquement destinée à être utilisée comme constructeur. Utilisez-vous une saveur inhabituelle comme ActionScript ou quelque chose?
AnthonyWJones
@Anthony, il vaut mieux utiliser var MYNAMESPACE = MYNAMESPACE || {}; simplement utiliser var myNamespace = {} n'est pas sûr et de plus il est préférable de déclarer votre espace de noms en majuscules
paul
9
@paul: "Mieux" peut être assez subjectif. Je déteste lire du code qui DEVRAIT m'être adressé, donc j'évite d'utiliser des identifiants qui utilisent tous les majuscules. Bien que cela ns = ns || {}puisse paraître plus défensif, cela peut entraîner d'autres résultats inattendus.
AnthonyWJones
32

Il s'agit d'un suivi du lien de user106826 vers Namespace.js. Il semble que le projet ait été déplacé vers GitHub . C'est maintenant smith / namespacedotjs .

J'utilise cet assistant JavaScript simple pour mon petit projet et jusqu'à présent, il semble être léger mais suffisamment polyvalent pour gérer l'espacement des noms et le chargement des modules / classes. Ce serait formidable si cela me permettait d'importer un package dans un espace de noms de mon choix, pas seulement l'espace de noms global ... soupir, mais c'est d'ailleurs le point.

Il vous permet de déclarer l'espace de noms puis de définir des objets / modules dans cet espace de noms:

Namespace('my.awesome.package');
my.awesome.package.WildClass = {};

Une autre option consiste à déclarer l'espace de noms et son contenu à la fois:

Namespace('my.awesome.package', {
    SuperDuperClass: {
        saveTheDay: function() {
            alert('You are welcome.');
        }
    }
});

Pour plus d'exemples d'utilisation, consultez le fichier example.js dans la source .

Rudy Lattae
la source
2
Tant que vous vous en souvenez, cela a des implications en termes de performances, car chaque fois que vous accédez à my.awesome.package.WildClass, vous accédez à la propriété awesome de my, à la propriété package de my.awesome et à la propriété WildClass de my.awesome. paquet.
SamStephens
29

Échantillon:

var namespace = {};
namespace.module1 = (function(){

    var self = {};
    self.initialized = false;

    self.init = function(){
        setTimeout(self.onTimeout, 1000)
    };

    self.onTimeout = function(){
        alert('onTimeout')
        self.initialized = true;
    };

    self.init(); /* If it needs to auto-initialize, */
    /* You can also call 'namespace.module1.init();' from outside the module. */
    return self;
})()

Vous pouvez éventuellement déclarer une localvariable,, samecomme selfet affecter local.onTimeoutsi vous voulez qu'elle soit privée.

Jim Jose
la source
14

Vous pouvez déclarer une fonction simple pour fournir des espaces de noms.

function namespace(namespace) {
    var object = this, tokens = namespace.split("."), token;

    while (tokens.length > 0) {
        token = tokens.shift();

        if (typeof object[token] === "undefined") {
            object[token] = {};
        }

        object = object[token];
    }

    return object;
}

// Usage example
namespace("foo.bar").baz = "I'm a value!";
dnemoga
la source
13

Si vous avez besoin de la portée privée:

var yourNamespace = (function() {

  //Private property
  var publicScope = {};

  //Private property
  var privateProperty = "aaa"; 

  //Public property
  publicScope.publicProperty = "bbb";

  //Public method
  publicScope.publicMethod = function() {
    this.privateMethod();
  };

  //Private method
  function privateMethod() {
    console.log(this.privateProperty);
  }

  //Return only the public parts
  return publicScope;
}());

yourNamespace.publicMethod();

sinon si vous n'utilisez jamais la portée privée:

var yourNamespace = {};

yourNamespace.publicMethod = function() {
    // Do something...
};

yourNamespace.publicMethod2 = function() {
    // Do something...
};

yourNamespace.publicMethod();
Tadej
la source
12

Le modèle de module a été initialement défini comme un moyen de fournir à la fois l'encapsulation privée et publique pour les classes en génie logiciel conventionnel.

Lorsque vous travaillez avec le modèle de module, nous pouvons trouver utile de définir un modèle simple que nous utilisons pour commencer à l'utiliser. En voici une qui couvre l'espacement des noms, les variables publiques et privées.

En JavaScript, le modèle de module est utilisé pour émuler davantage le concept de classes de manière à ce que nous puissions inclure à la fois des méthodes publiques / privées et des variables dans un seul objet, protégeant ainsi des parties particulières de la portée globale. Il en résulte une réduction de la probabilité que nos noms de fonctions entrent en conflit avec d'autres fonctions définies dans des scripts supplémentaires sur la page.

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();

Les avantages

pourquoi le modèle de module est-il un bon choix? Pour commencer, c'est beaucoup plus propre pour les développeurs venant d'un arrière-plan orienté objet que l'idée d'une véritable encapsulation, au moins du point de vue JavaScript.

Deuxièmement, il prend en charge les données privées - ainsi, dans le modèle de module, les parties publiques de notre code peuvent toucher les parties privées, mais le monde extérieur est incapable de toucher les parties privées de la classe.

Désavantages

Les inconvénients du modèle de module sont que, comme nous accédons aux membres publics et privés différemment, lorsque nous souhaitons modifier la visibilité, nous devons en fait apporter des modifications à chaque endroit où le membre a été utilisé.

Nous ne pouvons pas non plus accéder aux membres privés dans les méthodes qui sont ajoutées à l'objet ultérieurement . Cela dit, dans de nombreux cas, le modèle de module est encore très utile et lorsqu'il est utilisé correctement, il a certainement le potentiel d'améliorer la structure de notre application.

Le modèle de module révélateur

Maintenant que nous sommes un peu plus familiers avec le modèle de module, jetons un coup d'œil à une version légèrement améliorée - le modèle de module révélateur de Christian Heilmann.

Le motif du module révélateur est né car Heilmann était frustré par le fait qu'il devait répéter le nom de l'objet principal lorsque nous voulions appeler une méthode publique à partir d'une autre ou accéder à des variables publiques. pour objecter la notation littérale des choses qu'il souhaitait rendre publiques.

Le résultat de ses efforts a été un modèle mis à jour où nous définirions simplement toutes nos fonctions et variables dans la portée privée et retournerions un objet anonyme avec des pointeurs à la fonctionnalité privée que nous souhaitions révéler comme publique.

Vous trouverez ci-dessous un exemple d'utilisation du modèle de module révélateur.

var myRevealingModule = (function () {

        var privateVar = "Ben Cherry",
            publicVar = "Hey there!";

        function privateFunction() {
            console.log( "Name:" + privateVar );
        }

        function publicSetName( strName ) {
            privateVar = strName;
        }

        function publicGetName() {
            privateFunction();
        }


        // Reveal public pointers to
        // private functions and properties

        return {
            setName: publicSetName,
            greeting: publicVar,
            getName: publicGetName
        };

    })();

myRevealingModule.setName( "Paul Kinlan" );

Les avantages

Ce modèle permet à la syntaxe de nos scripts d'être plus cohérente. Il indique également plus clairement à la fin du module quelles sont nos fonctions et variables accessibles au public, ce qui facilite la lisibilité.

Désavantages

Un inconvénient de ce modèle est que si une fonction privée fait référence à une fonction publique, cette fonction publique ne peut pas être remplacée si un correctif est nécessaire. En effet, la fonction privée continuera de faire référence à l'implémentation privée et le modèle ne s'applique pas aux membres publics, uniquement aux fonctions.

Les membres d'objets publics qui font référence à des variables privées sont également soumis aux notes de règle sans correctif ci-dessus.

Divyanshu Rawat
la source
9

J'ai créé un espace de noms inspiré des modules d'Erlang. C'est une approche très fonctionnelle, mais c'est ainsi que j'écris mon code JavaScript de nos jours.

Il donne à une fermeture un espace de noms global et expose un ensemble défini de fonctions au sein de cette fermeture.

(function(){

  namespace("images", previous, next);
  // ^^ This creates or finds a root object, images, and binds the two functions to it.
  // It works even though those functions are not yet defined.

  function previous(){ ... }

  function next(){ ... }

  function find(){ ... } // A private function

})();
L'OMS
la source
8

Après avoir porté plusieurs de mes bibliothèques sur différents projets et avoir constamment changé l'espace de noms de niveau supérieur (nommé statiquement), je suis passé à l'utilisation de cette petite fonction d'assistance (open source) pour définir les espaces de noms.

global_namespace.Define('startpad.base', function(ns) {
    var Other = ns.Import('startpad.other');
    ....
});

La description des avantages se trouve sur mon blog . Vous pouvez récupérer le code source ici .

L'un des avantages que j'aime vraiment est l'isolation entre les modules en ce qui concerne l'ordre de chargement. Vous pouvez vous référer à un module externe AVANT qu'il ne soit chargé. Et la référence d'objet que vous obtenez sera remplie lorsque le code sera disponible.

mckoss
la source
1
J'ai créé une version améliorée (2.0) de la bibliothèque d'espace de noms: code.google.com/p/pageforest/source/browse/appengine/static/src/…
mckoss
tous vos liens semblent morts
snoob dogg
8

J'utilise la syntaxe suivante pour l'espace de noms.

var MYNamespace = MYNamespace|| {};

 MYNamespace.MyFirstClass = function (val) {
        this.value = val;
        this.getValue = function(){
                          return this.value;
                       };
    }

var myFirstInstance = new MYNamespace.MyFirstClass(46);
alert(myFirstInstance.getValue());

jsfiddle: http://jsfiddle.net/rpaul/4dngxwb3/1/

Razan Paul
la source
8

J'ai 7 ans de retard à la fête, mais j'ai fait pas mal de travail il y a 8 ans:

Il est important de pouvoir créer facilement et efficacement plusieurs espaces de noms imbriqués pour garder une application Web complexe organisée et gérable, tout en respectant l'espace de noms global JavaScript (empêchant la pollution des espaces de noms), et sans encombrer aucun objet existant dans le chemin de l'espace de noms en le faisant. .

De ce qui précède, c'était ma solution vers 2008:

var namespace = function(name, separator, container){
  var ns = name.split(separator || '.'),
    o = container || window,
    i,
    len;
  for(i = 0, len = ns.length; i < len; i++){
    o = o[ns[i]] = o[ns[i]] || {};
  }
  return o;
};

Ce n'est pas la création d'un espace de noms, mais fournit une fonction pour créer des espaces de noms.

Celui-ci peut être condensé en une doublure minifiée:

var namespace=function(c,f,b){var e=c.split(f||"."),g=b||window,d,a;for(d=0,a=e.length;d<a;d++){g=g[e[d]]=g[e[d]]||{}}return g};

Exemple d'utilisation:

namespace("com.example.namespace");
com.example.namespace.test = function(){
  alert("In namespaced function.");
};

Ou, comme une seule déclaration:

namespace("com.example.namespace").test = function(){
  alert("In namespaced function.");
};

L'un ou l'autre est alors exécuté comme:

com.example.namespace.test();

Si vous n'avez pas besoin de prise en charge des navigateurs hérités, une version mise à jour:

const namespace = function(name, separator, container){
    var o = container || window;
    name.split(separator || '.').forEach(function(x){
        o = o[x] = o[x] || {};
    });
    return o;
};

Maintenant, je me méfierais de l'exposer namespaceà l'espace de noms global lui-même. (Dommage que la langue de base ne nous fournisse pas cela!) Donc, je l'utilise généralement moi-même dans une fermeture, comme:

(function(){
	const namespace = function(name, separator, container){
		var o = container || window;
		name.split(separator || '.').forEach(function(x){
			o = o[x] = o[x] || {};
		});
		return o;
	};
	const ns = namespace("com.ziesemer.myApp");
	
	// Optional:
	ns.namespace = ns;
	
	// Further extend, work with ns from here...
}());

console.log("\"com\":", com);

Dans une application plus grande, cela ne doit être défini qu'une seule fois au début du chargement d'une page (pour les applications Web clientes). Des fichiers supplémentaires peuvent ensuite réutiliser la fonction d'espace de noms s'ils sont conservés (inclus comme "facultatif" dans ce qui précède). Au pire, si cette fonction est re-déclarée plusieurs fois - ce ne sont que quelques lignes de code, et moins si elle est minifiée.

ziesemer
la source
3

Je pense que vous utilisez tous trop de code pour un problème aussi simple. Pas besoin de faire un repo pour ça. Voici une fonction sur une seule ligne.

namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window);

Essayez-le:

// --- definition ---
const namespace = namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window);

// --- Use ----
let myNamespace = namespace("a.b.c");
myNamespace.MyClass = class MyClass {};

// --- see ----
console.log("a : ", a);

Yairopro
la source
2

J'aime la solution de Jaco Pretorius, mais je voulais rendre le mot clé "this" un peu plus utile en le pointant sur l'objet module / namespace. Ma version de skillet:

(function ($, undefined) {

    console.log(this);

}).call(window.myNamespace = window.myNamespace || {}, jQuery);
haxpanel
la source
2

Mon modèle préféré est devenu récemment ceci:

var namespace = (function() {
  
  // expose to public
  return {
    a: internalA,
    c: internalC
  }

  // all private
  
  /**
   * Full JSDoc
   */
  function internalA() {
    // ...
  }
  
  /**
   * Full JSDoc
   */
  function internalB() {
    // ...
  }
  
  /**
   * Full JSDoc
   */
  function internalC() {
    // ...
  }
  
  /**
   * Full JSDoc
   */
  function internalD() {
    // ...
  }
  
})();

Bien sûr, le retour peut être à la fin, mais si seules les déclarations de fonction le suivent, il est beaucoup plus facile de voir de quoi parle l'espace de noms et quelle API est exposée.

Le modèle d'utilisation des expressions de fonction dans de tels cas se traduit par l'impossibilité de savoir quelles méthodes sont exposées sans parcourir tout le code.

Nomaed
la source
Salut, comment appelez-vous les fonctions publiques à partir de votre extrait? J'ai essayénamespace.a();
olimart
@ olivier oui, c'est l'idée. Bien que maintenant avec ES6, j'utilise généralement la syntaxe abrégée des littéraux d'objet ( ponyfoo.com/articles/es6-object-literal-features-in-depth )
Nomaed
1

Si vous utilisez un Makefile, vous pouvez le faire.

// prelude.hjs
billy = new (
    function moduleWrapper () {
    const exports = this;

// postlude.hjs
return exports;
})();

// someinternalfile.js
function bob () { console.log('hi'); }
exports.bob = bob;

// clientfile.js
billy.bob();

Je préfère de toute façon utiliser un Makefile une fois que j'arrive à environ 1000 lignes car je peux efficacement commenter de grandes portions de code en supprimant une seule ligne dans le makefile. Il est facile de jouer avec des trucs. De plus, avec cette technique, l'espace de noms n'apparaît qu'une seule fois dans le prélude, il est donc facile de le modifier et vous n'avez pas à le répéter dans le code de la bibliothèque.

Un script shell pour le développement en direct dans le navigateur lors de l'utilisation d'un makefile:

while (true); do make; sleep 1; done

Ajoutez ceci en tant que tâche de création «go» et vous pouvez «créer» pour garder votre build à jour pendant que vous codez.

Samuel Danielson
la source
1

Tout à fait un suivi de la réponse d'Ionuț G. Stan, mais montrant les avantages du code épuré en utilisant var ClassFirst = this.ClassFirst = function() {...}, qui tire parti de la portée de fermeture de JavaScript pour moins d'encombrement de l'espace de noms pour les classes du même espace de noms.

var Namespace = new function() {
    var ClassFirst = this.ClassFirst = function() {
        this.abc = 123;
    }

    var ClassSecond = this.ClassSecond = function() {
        console.log("Cluttered way to access another class in namespace: ", new Namespace.ClassFirst().abc);
        console.log("Nicer way to access a class in same namespace: ", new ClassFirst().abc);
    }
}

var Namespace2 = new function() {
    var ClassFirst = this.ClassFirst = function() {
        this.abc = 666;
    }

    var ClassSecond = this.ClassSecond = function() {
        console.log("Cluttered way to access another class in namespace: ", new Namespace2.ClassFirst().abc);
        console.log("Nicer way to access a class in same namespace: ", new ClassFirst().abc);
    }
}

new Namespace.ClassSecond()
new Namespace2.ClassSecond()

Production:

Cluttered way to access another class in namespace: 123
Nicer way to access a class in same namespace: 123
Cluttered way to access another class in namespace: 666
Nicer way to access a class in same namespace: 666
lama12345
la source
1

J'ai écrit une autre bibliothèque d'espaces de noms qui fonctionne un peu plus comme les packages / unités dans d'autres langues. Il vous permet de créer un package de code JavaScript et la référence de ce package à partir d'un autre code:

Fichier hello.js

Package("hello", [], function() {
  function greeting() {
    alert("Hello World!");
  }
  // Expose function greeting to other packages
  Export("greeting", greeting);
});

Fichier Example.js

Package("example", ["hello"], function(greeting) {
  // Greeting is available here
  greeting();  // Alerts: "Hello World!"
});

Seul le deuxième fichier doit être inclus dans la page. Ses dépendances (fichier hello.js dans cet exemple) seront automatiquement chargées et les objets exportés à partir de ces dépendances seront utilisés pour remplir les arguments de la fonction de rappel.

Vous pouvez trouver le projet associé dans les packages JS .

Stijn de Witt
la source
1
@ peter-mortensen Ces modifications de ma réponse de '11 étaient-elles vraiment nécessaires? Ce n'est certainement pas du vandalisme ce que vous faites, ne vous méprenez pas, mais ils sont très superficiels. Je préférerais rester le seul auteur d'articles comme ceux-ci, à moins que vous ajoutiez vraiment quelque chose de bien.
Stijn de Witt
1

Nous pouvons l'utiliser indépendamment de cette manière:

var A = A|| {};
A.B = {};

A.B = {
    itemOne: null,
    itemTwo: null,
};

A.B.itemOne = function () {
    //..
}

A.B.itemTwo = function () {
    //..
}
ganesh
la source
0

Mon habitude est d'utiliser la fonction myName () comme stockage de propriété, puis var myName comme support de "méthode" ...

Que ce soit assez légitime ou non, battez-moi! Je m'appuie tout le temps sur ma logique PHP et les choses fonctionnent simplement. :RÉ

function myObj() {
    this.prop1 = 1;
    this.prop2 = 2;
    this.prop3 = 'string';
}

var myObj = (
 (myObj instanceof Function !== false)
 ? Object.create({

     $props: new myObj(),
     fName1: function() { /* code..  */ },
     fName2: function() { /* code ...*/ }
 })
 : console.log('Object creation failed!')
);

if (this !== that) myObj.fName1(); else myObj.fName2();

Vous pouvez également le faire de manière «vice versa» pour vérifier avant la création d'objet, ce qui est bien mieux :

function myObj() {
    this.prop1 = 1;
    this.prop2 = 2;
    this.prop3 = 'string';
}

var myObj = (
    (typeof(myObj) !== "function" || myObj instanceof Function === false)
    ? new Boolean()
    : Object.create({
        $props: new myObj(),
        init: function () { return; },
        fName1: function() { /* code..  */ },
        fName2: function() { /* code ...*/ }
    })
);

if (myObj instanceof Boolean) {
    Object.freeze(myObj);
    console.log('myObj failed!');
    debugger;
}
else
    myObj.init();

Référence à ceci: JavaScript: création d'objet avec Object.create ()

Effrayant
la source
0

En JavaScript, il n'y a pas de méthodes prédéfinies pour utiliser les espaces de noms. En JavaScript, nous devons créer nos propres méthodes pour définir les NameSpaces. Voici une procédure que nous suivons dans les technologies Oodles.

Enregistrer un espace de noms Voici la fonction pour enregistrer un espace de noms

//Register NameSpaces Function
function registerNS(args){
 var nameSpaceParts = args.split(".");
 var root = window;

 for(var i=0; i < nameSpaceParts.length; i++)
 {
  if(typeof root[nameSpaceParts[i]] == "undefined")
   root[nameSpaceParts[i]] = new Object();

  root = root[nameSpaceParts[i]];
 }
}

Pour enregistrer un espace de noms, il suffit d'appeler la fonction ci-dessus avec l'argument comme espace de noms séparé par '.'(point). Par exemple, laissez le nom de votre application est oodles. Vous pouvez créer un espace de noms en suivant la méthode

registerNS("oodles.HomeUtilities");
registerNS("oodles.GlobalUtilities");
var $OHU = oodles.HomeUtilities;
var $OGU = oodles.GlobalUtilities;

Fondamentalement, il créera votre structure NameSpaces comme ci-dessous dans le backend:

var oodles = {
    "HomeUtilities": {},
    "GlobalUtilities": {}
};

Dans la fonction ci-dessus, vous avez enregistré un espace de noms appelé "oodles.HomeUtilities"et "oodles.GlobalUtilities". Pour appeler ces espaces de noms, nous créons une variable, c'est-à-dire var $OHUet var $OGU.

Ces variables ne sont qu'un alias pour intialiser l'espace de noms. Maintenant, chaque fois que vous déclarez une fonction qui vous appartient, HomeUtilitiesvous la déclarerez comme suit:

$OHU.initialization = function(){
    //Your Code Here
};

Ci-dessus se trouve l'initialisation du nom de la fonction et elle est placée dans un espace de noms $OHU. et d'appeler cette fonction n'importe où dans les fichiers de script. Utilisez simplement le code suivant.

$OHU.initialization();

De même, avec les autres NameSpaces.

J'espère que cela aide.

Saurabh tiwary
la source
0

JavaScript ne prend pas en charge l'espace de noms par défaut. Donc, si vous créez un élément (fonction, méthode, objet, variable), il devient global et pollue l'espace de noms global. Prenons un exemple de définition de deux fonctions sans espace de noms,

function func1() {
    console.log("This is a first definition");

}
function func1() {
    console.log("This is a second definition");
}
func1(); // This is a second definition

Il appelle toujours la deuxième définition de fonction. Dans ce cas, l'espace de noms résoudra le problème de collision de noms.

vishu2124
la source