Les variables globales doivent être réservées aux objets ayant des dépendances à l'échelle du système, et leur dénomination doit éviter toute ambiguïté et minimiser le risque de conflits de noms. En pratique, cela signifie que vous devez éviter de créer des objets globaux à moins qu'ils ne soient absolument nécessaires.
Mais bon, vous le saviez déjà...
Alors, qu'avez-vous fait à ce sujet ? Les approches traditionnelles nous disent que la meilleure stratégie pour éliminer les globaux consiste à créer un petit nombre d'objets globaux qui servent d'espaces de noms réels pour les modules et sous-systèmes sous-jacents. J'explorerai plusieurs approches des espaces de noms et terminerai par une solution élégante, sûre et flexible que j'ai proposée sur la base d'un article récent de James Edwards.
J'utilise 静态命名空间
comme terme générique pour les solutions où les balises d'espace de noms sont en fait codées en dur. Oui, vous pouvez réattribuer un espace de noms à un autre, mais le nouvel espace de noms fera référence aux mêmes objets que l'ancien.
La méthode la plus basique. C'est très verbeux, et si vous souhaitez renommer ces espaces de noms, vous avez du travail à faire. Mais c'est sûr et clair.
var myApp = {} myApp.id = 0; myApp.next = function() { return myApp.id++; } myApp.reset = function() { myApp.id = 0; } window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ); //0, 1, undefined, 0
Vous pouvez également faciliter la maintenance future en utilisant this
pour référencer les propriétés frères, mais c'est un peu risqué car rien n'empêche la réaffectation de vos méthodes d'espace de noms.
var myApp = {} myApp.id = 0; myApp.next = function() { return this.id++; } myApp.reset = function() { this.id = 0; } myApp.next(); //0 myApp.next(); //1 var getNextId = myApp.next; getNextId(); //NaN whoops!
Maintenant, nous n'avons besoin de référencer le nom de l'espace de noms qu'une seule fois, il est donc plus facile de changer le nom plus tard (en supposant que vous n'ayez pas référencé l'espace de noms à plusieurs reprises ) . Il existe toujours un risque que la valeur de this
provoque une "surprise" - mais il est plus sûr de supposer que l'objet défini dans une structure littérale d'objet ne sera pas réaffecté.
var myApp = { id: 0, next: function() { return this.id++; }, reset: function() { this.id = 0; } } window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ) //0, 1, undefined, 0
Je me retrouve à utiliser 模块模式
plus récemment. La logique est isolée de la portée globale par un wrapper de méthode (généralement auto-invoquant), qui renvoie un objet représentant l'interface publique du module. En appelant immédiatement cette méthode et en attribuant le résultat à une variable d'espace de noms, nous verrouillons l'API du module dans cette variable nommée. De plus, toutes les variables non incluses dans la valeur de retour resteront toujours privées, visibles uniquement par les méthodes publiques qui les référencent.
var myApp = (function() { var id= 0; return { next: function() { return id++; }, reset: function() { id = 0; } }; })(); window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ) //0, 1, undefined, 0
Comme pour l'exemple de littéral d'objet ci-dessus, le nom de l'espace de noms peut être facilement modifié, mais il y a un avantage supplémentaire : les littéraux d'objet sont de classe 4 – tout est question d'attribution d'attributs, pas de place pour la logique de prise en charge. De plus, toutes les propriétés doivent être initialisées et les valeurs des propriétés ne peuvent pas être facilement référencées entre les objets (ainsi, par exemple, les fermetures internes ne sont pas possibles). Le modèle de module ne présente aucune des contraintes ci-dessus et nous offre des avantages supplémentaires en matière de confidentialité.
Nous pouvons également appeler cette section 命名空间注入
. L'espace de noms est représenté par un proxy qui fait directement référence au wrapper de méthode 内部
– cela signifie que nous n'avons plus besoin d'envelopper la valeur de retour attribuée à l'espace de noms. Cela rend les définitions d'espace de noms plus flexibles et permet d'avoir plusieurs instances indépendantes du module qui existent dans des espaces de noms indépendants (ou même dans le contexte global). Les espaces de noms dynamiques prennent en charge toutes les fonctionnalités du modèle de module avec l'avantage supplémentaire d'être intuitifs et lisibles.
Ici, nous passons simplement l'espace de noms en tant que paramètre à la méthode auto-appelante. La variable id
est privée car elle n'est pas affectée à context
.
var myApp = {}; (function(context) { var id = 0; context.next = function() { return id++; }; context.reset = function() { id = 0; } })(myApp); window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ) //0, 1, undefined, 0
Nous pouvons même définir context
sur un objet global (avec un seul changement de mot !). C'est un énorme atout pour les propriétaires de bibliothèques : ils peuvent regrouper leurs fonctionnalités dans une fonction auto-appelante et laisser l'utilisateur décider si elles sont globales ou non (John Resig a été l'un des premiers à adopter cette théorie lorsqu'il écrivait JQuery).
var myApp = {}; (function(context) { var id = 0; context.next = function() { return id++; }; context.reset = function() { id = 0; } })(this); window.console && console.log( next(), next(), reset(), next() ) //0, 1, undefined, 0
this
comme proxy d'espace de nomsUn article récent de James Edwads a piqué mon intérêt. "Mon modèle de conception JavaScript préféré" a apparemment été mal compris par de nombreux commentateurs, qui pensent qu'il peut également s'appuyer sur le modèle de module. Cet article fait la promotion d'une variété de techniques (ce qui déroute probablement le lecteur), mais il y a à la base un peu de génie que j'ai modifié et présenté comme un outil d'espace de noms.
La beauté de ce modèle est qu'il est utilisé simplement de la manière dont le langage a été conçu - ni plus, ni moins, pas d'opportunisme, pas de triche. De plus, comme l'espace de noms est injecté via le mot-clé this
(qui est immuable dans un contexte d'exécution donné), il ne peut pas être modifié accidentellement.
var myApp = {}; (function() { var id = 0; this.next = function() { return id++; }; this.reset = function() { id = 0; } }).apply(myApp); window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ); //0, 1, undefined, 0
Mieux encore, l'API apply
(et call
) offre une isolation naturelle du contexte et des paramètres - donc transmettre des paramètres supplémentaires au créateur du module est très propre. L'exemple suivant illustre cela et montre comment exécuter des modules indépendamment de plusieurs espaces de noms.
var subsys1 = {}, subsys2 = {}; var nextIdMod = function(startId) { var id = startId || 0; this.next = function() { return id++; }; this.reset = function() { id = 0; } }; nextIdMod.call(subsys1); nextIdMod.call(subsys2,1000); window.console && console.log( subsys1.next(), subsys1.next(), subsys2.next(), subsys1.reset(), subsys2.next(), subsys1.next() ) //0, 1, 1000, undefined, 1001, 0
Bien sûr si nous avons besoin d'un générateur d'identifiant global, c'est très simple...
nextIdMod(); window.console && console.log( next(), next(), reset(), next() ) //0, 1, undefined, 0
这个我们作为例子使用的 id 生成器工具并没有表现出这个模式的全部潜力。通过包裹一整个库和使用this
关键字作为命名空间的替身,我们使得用户在任何他们选择的上下文中运行这个库很轻松(包括全局上下文)。
//library code var protoQueryMooJo = function() { //everything } //user code var thirdParty = {}; protoQueryMooJo.apply(thirdParty);
我希望避免命名空间嵌套。它们很难追踪(对人和电脑都是)并且它们会让你的代码因为一些乱七八糟的东西变得很多。如 Peter Michaux 指出的,深度嵌套的命名空间可能是那些视图重新创建他们熟悉和热爱的长包链的老派 Java 开发者的遗产。
通过 .js 文件来固定一个单独的命名空间也是可以的(虽然只能通过命名空间注入或者直接分配每一个变量),不过你应该对依赖谨慎些。此外将命名空间绑定到文件上可以帮助读者更轻易弄清整个代码。
因为 JavaScript 并没有正式的命名空间结构,所以有很多自然形成的方法。这个调查只详细说明了其中的一部分,可能有更好的技术我没有发现。我很乐意知道它们。
以上就是JavaScript 中的命名空间详细介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!