How do I declare a namespace in JavaScript?
如何在javascript中创建一个名称空间,以便我的对象和函数不会被其他相同的命名对象和函数覆盖?我使用了以下方法:
1 | if (Foo == null || typeof(Foo) !="object") { var Foo = new Object();} |
有没有一种更优雅或更简洁的方法?
我使用企业jquery网站上的方法:
下面是它们的示例,演示如何声明私有和公共属性和函数。一切都是作为一个自动执行的匿名函数完成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | (function( skillet, $, undefined ) { //Private Property var isHot = true; //Public Property skillet.ingredient ="Bacon Strips"; //Public Method skillet.fry = function() { var oliveOil; addItem("\t Butter \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 )); |
所以,如果你想接触其中一个公众成员,你只需要去
真正酷的是,您现在可以使用完全相同的语法扩展名称空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 | //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 )); |
第三个
The third,
undefined argument is the source of the variable of valueundefined . I'm not sure if it's still relevant today, but while working with older browsers / JavaScript standards (ecmascript 5, javascript < 1.8.5 ~ firefox 4), the global-scope variableundefined is writable, so anyone could rewrite its value. The third argument (when not passed a value) creates a variable namedundefined which is scoped to the namespace/function. Because no value was passed when you created the name space, it defaults to the valueundefined .
我喜欢这个:
1 2 3 4 5 6 7 8 9 10 11 12 | var yourNamespace = { foo: function() { }, bar: function() { } }; ... yourNamespace.foo(); |
另一种方法是这样做,我认为它比对象文本形式的限制要小一些,这是:
1 2 3 4 5 6 7 8 9 10 | var ns = new function() { var internalFunction = function() { }; this.publicFunction = function() { }; }; |
上面的模式与模块模式非常相似,无论您喜欢与否,它都允许您将所有函数公开为公共函数,同时避免对象文本的刚性结构。
Is there a more elegant or succinct way of doing this?
对。例如:
1 | var your_namespace = your_namespace || {}; |
那你就可以
1 2 3 4 5 6 7 8 9 10 | var your_namespace = your_namespace || {}; your_namespace.Foo = {toAlert:'test'}; your_namespace.Bar = function(arg) { alert(arg); }; with(your_namespace) { Bar(Foo.toAlert); } |
我通常在一个闭包中构建它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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"; } }; })(); |
这些年来,我的风格在写这篇文章之后发生了微妙的变化,现在我发现自己在写这篇文章的结尾:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 }; })(); |
这样,我发现公共API和实现更容易理解。把返回语句看作是实现的公共接口。
因为您可能会编写不同的javascript文件,稍后在应用程序中合并或不合并它们,所以每个文件都需要能够恢复或构造命名空间对象,而不会损坏其他文件的工作…
一个文件可能打算使用名称空间
1 2 3 4 | namespace = window.namespace || {}; namespace.namespace1 = namespace.namespace1 || {}; namespace.namespace1.doSomeThing = function(){} |
另一个文件可能需要使用名称空间
1 2 3 4 | namespace = window.namespace || {}; namespace.namespace2 = namespace.namespace2 || {}; namespace.namespace2.doSomeThing = function(){} |
这两个文件可以共存或分离而不会发生碰撞。
下面是Stoyan Stefanov在他的javascript模式书中的做法,我发现这本书非常好(它还展示了他如何处理允许自动生成API文档的注释,以及如何将方法添加到自定义对象的原型中):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | /** * 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; }; |
我使用这种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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() |
外部代码可以是:
1 2 3 | var myClass = new myNamespace.MyClass(); var myOtherClass = new myNamepace.subName.SomeOtherClass(); myNamespace.subName.publicOtherStuff(someParameter); |
这是对user106826指向namespace.js的链接的后续操作。项目似乎转移到了Github。现在是smith/namespacedotjs。
我一直在为我的小项目使用这个简单的javascript助手,到目前为止,它看起来很轻,但功能丰富,足以处理名称间距和加载模块/类。如果它允许我将包导入到我选择的命名空间中,而不仅仅是全局命名空间,那就太好了。叹气,但这不是重点。
它允许您声明命名空间,然后在该命名空间中定义对象/模块:
1 2 | Namespace('my.awesome.package'); my.awesome.package.WildClass = {}; |
另一种选择是立即声明命名空间及其内容:
1 2 3 4 5 6 7 | Namespace('my.awesome.package', { SuperDuperClass: { saveTheDay: function() { alert('You are welcome.'); } } }); |
有关更多用法示例,请查看源文件中的example.js文件。
Sample:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; })() |
您可以选择声明一个
您可以声明一个简单的函数来提供名称空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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!"; |
如果需要专用范围:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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(); |
否则,如果您不使用私有范围:
1 2 3 4 5 6 7 8 9 10 11 | var yourNamespace = {}; yourNamespace.publicMethod = function() { // Do something... }; yourNamespace.publicMethod2 = function() { // Do something... }; yourNamespace.publicMethod(); |
我创建了受Erlang模块启发的名称空间。这是一种非常实用的方法,但这就是我最近编写JavaScript代码的方式。
它为一个闭包提供一个全局命名空间,并在该闭包中公开一个已定义的集合函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 | (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 })(); |
我对名称空间使用以下语法。
1 2 3 4 5 6 7 8 9 10 11 | 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()); |
jfiddle:http://jsfiddle.net/rpaul/4dngxwb3/1/
模块模式最初被定义为一种为传统软件工程中的类提供私有和公共封装的方法。
在使用模块模式时,我们可能会发现定义一个简单的模板是很有用的,我们使用它来开始使用它。这是一个包含名称间距、公共和私有变量的函数。
在JavaScript中,模块模式用于进一步模拟类的概念,这样我们就可以在单个对象中同时包含公共/私有方法和变量,从而将特定部分从全局范围中屏蔽。这样做的结果是减少了函数名与页面上其他脚本中定义的其他函数冲突的可能性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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 ); } }; })(); |
优势
为什么模块模式是一个好的选择?对于初学者来说,对于来自面向对象背景的开发人员来说,它要比真正封装的思想干净得多,至少从JavaScript的角度来看是这样。
其次,它支持私有数据——因此,在模块模式中,代码的公共部分能够接触私有部分,但是外部世界无法接触类的私有部分。
缺点
模块模式的缺点是,当我们以不同的方式访问公共成员和私有成员时,当我们希望更改可见性时,实际上必须对成员使用的每个位置进行更改。
在稍后添加到对象的方法中,我们也无法访问私有成员。也就是说,在许多情况下,模块模式仍然非常有用,如果正确使用,当然有潜力改进我们的应用程序的结构。
揭示模块模式
现在我们对模块模式更加熟悉了,让我们来看一个稍微改进的版本——Christian Heilmann的揭示模块模式。
当我们想从另一个公共方法调用一个公共方法或访问公共变量时,Heilmann不得不重复主对象的名称,这一事实使他感到沮丧,他也不喜欢模块模式要求必须切换到对象文字符号,以表示他希望发布的内容。集成电路。
他的工作的结果是更新了模式,我们只需在私有范围内定义所有的函数和变量,并返回一个匿名对象,其中包含指向我们希望公开的私有功能的指针。
下面提供了一个如何使用揭示模块模式的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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" ); |
优势
这种模式允许脚本的语法更加一致。在模块的末尾,它也更清楚地说明了哪些函数和变量可以公开访问,从而降低了可读性。
缺点
这种模式的一个缺点是,如果一个私有函数引用了一个公共函数,那么如果需要一个补丁,这个公共函数就不能被重写。这是因为私有函数将继续引用私有实现,并且模式不适用于公共成员,只适用于函数。
引用私有变量的公共对象成员也受制于上面的无修补程序规则注释。
型
我迟到了7年,但在这8年前做了不少工作:
- 百万千克1http://blogger.ziesemer.com/2008/05/javascript-namespace-function.html百万千克1百万千克1http://blogger.ziesemer.com/2007/10/eforing-javascript-global-namespace.html百万千克1
型
必须能够轻松高效地创建多个嵌套的命名空间,以保持复杂的Web应用程序的组织和管理,同时尊重JavaScript全局命名空间(防止命名空间污染),并且在执行此操作时不要破坏命名空间路径中的任何现有对象。
从上面看,这是我的Circa-2008解决方案:
1 2 3 4 5 6 7 8 9 10 | 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; }; |
这不是创建名称空间,而是提供用于创建名称空间的函数。
这可以压缩成一个小型的一行程序:
1 | 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}; |
号
使用示例:
1 2 3 4 | namespace("com.example.namespace"); com.example.namespace.test = function(){ alert("In namespaced function."); }; |
或者,作为一种陈述:
1 2 3 | namespace("com.example.namespace").test = function(){ alert("In namespaced function."); }; |
。
然后执行任一操作:
1 | com.example.namespace.test(); |
如果不需要对旧版浏览器的支持,则更新后的版本:
1 2 3 4 5 6 7 | const namespace = function(name, separator, container){ var o = container || window; name.split(separator || '.').forEach(function(x){ o = o[x] = o[x] || {}; }); return o; }; |
。
现在,我对向全局名称空间本身公开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | (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); |
。
在较大的应用程序中,只需要在页面加载开始时定义一次(对于基于客户端的Web应用程序)。如果保留了名称空间函数(在上面作为"可选"包含),则其他文件可以重用该函数。最坏的情况是,如果这个函数被重新声明几次-它只有几行代码,如果缩小的话就更少了。
在将我的几个库移植到不同的项目中,并且必须不断地更改顶级(静态命名)命名空间之后,我已经改用这个小的(开源)助手函数来定义命名空间。
1 2 3 4 | global_namespace.Define('startpad.base', function(ns) { var Other = ns.Import('startpad.other'); .... }); |
我的博客上有关于这些好处的描述。您可以在这里获取源代码。
我真正喜欢的一个好处是模块之间的负载顺序隔离。您可以在加载外部模块之前引用它。当代码可用时,您得到的对象引用将被填充。
您必须检查namespace.js!
我最喜欢的图案最近变成了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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() { // ... } })(); |
当然,返回可以在末尾,但是如果只有函数声明跟在后面,那么更容易看到名称空间是关于什么的,以及API公开了什么。
在这种情况下,使用函数表达式的模式会导致在不遍历整个代码的情况下无法知道哪些方法是公开的。
我喜欢JacoPretorius的解决方案,但我想通过将"this"关键字指向module/namespace对象,使其更加有用。我的煎锅版本:
1 2 3 4 5 | (function ($, undefined) { console.log(this); }).call(window.myNamespace = window.myNamespace || {}, jQuery); |
我们可以这样独立地使用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var A = A|| {}; A.B = {}; A.B = { itemOne: null, itemTwo: null, }; A.B.itemOne = function () { //.. } A.B.itemTwo = function () { //.. } |
我认为对于这样一个简单的问题,你们都使用了太多的代码。没有必要为此做回购。这是一个单行函数。
1 | namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window); |
试试看:
1 2 3 4 5 6 7 8 9 | // --- 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); |
对爱奥努的后续调查?g.Stan的答案,但使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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() |
输出:
1 2 3 4 | 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 |
如果使用makefile,可以这样做。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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(); |
我更喜欢使用makefile,因为我可以通过删除makefile中的一行来有效地注释大量代码。它使摆弄东西变得容易。而且,使用这种技术,名称空间只在序曲中出现一次,因此很容易更改,而且不必在库代码中不断重复它。
当使用makefile时,浏览器中用于实时开发的shell脚本:
1 | while (true); do make; sleep 1; done |
将此添加为生成任务"go",您可以"make go"以在编写代码时保持生成更新。
在JavaScript中,没有使用名称空间的预定义方法。在JavaScript中,我们必须创建自己的方法来定义名称空间。这是我们在Oodles技术中遵循的一个过程。
注册命名空间下面是注册名称空间的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 | //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]]; } } |
要注册一个名称空间,只需使用参数作为名称空间调用上面的函数,名称空间由
1 2 3 4 | registerNS("oodles.HomeUtilities"); registerNS("oodles.GlobalUtilities"); var $OHU = oodles.HomeUtilities; var $OGU = oodles.GlobalUtilities; |
基本上,它将在后端创建如下名称空间结构:
1 2 3 4 | var oodles = { "HomeUtilities": {}, "GlobalUtilities": {} }; |
在上面的函数中,您注册了一个名为
这些变量只是名称空间初始化的别名。现在,每当您声明一个属于
1 2 3 | $OHU.initialization = function(){ //Your Code Here }; |
上面是函数名初始化,它被放入名称空间
1 | $OHU.initialization(); |
类似地,使用另一个名称空间。
希望它有帮助。
我的习惯是使用函数myname()作为属性存储,然后将varmyname作为"方法"持有者…
不管这是否合法,打败我!我一直都在依赖我的PHP逻辑,而且事情很简单。D
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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!') ); |
You can also do it in a 'vice versa' way to check before object creation which is much better:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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(); |
引用此:javascript:使用object.create()创建对象
我已经编写了另一个名称空间库,它的工作方式与其他语言中的包/单元有点类似。它允许您创建一个JavaScript代码包,并从其他代码引用该包:
文件Helo.js1 2 3 4 5 6 7 | Package("hello", [], function() { function greeting() { alert("Hello World!"); } // Expose function greeting to other packages Export("greeting", greeting); }); |
文件示例
1 2 3 4 | Package("example", ["hello"], function(greeting) { // Greeting is available here greeting(); // Alerts:"Hello World!" }); |
页面中只需要包含第二个文件。它的依赖项(本例中的文件hello.js)将自动加载,从这些依赖项导出的对象将用于填充回调函数的参数。
您可以在包JS中找到相关的项目。