我如何在JavaScript中声明一个名称空间?
如何在JavaScript中创build一个名称空间,以便我的对象和函数不被其他同名对象和函数覆盖? 我用了以下内容:
if (Foo == null || typeof(Foo) != "object") { var Foo = new Object();}
有没有更优雅或简洁的方式呢?
我喜欢这个:
var yourNamespace = { foo: function() { }, bar: function() { } }; ... yourNamespace.foo();
我使用的企业jQuery网站上find的方法 :
以下是他们的示例,展示了如何声明私有和公共属性和函数。 一切都是作为一个自我执行的匿名函数完成的。
(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 ));
所以如果你想访问一个公共成员,你只需要去skillet.fry()
或者skillet.ingredients
。
真正好的是现在你可以使用完全相同的语法来扩展名字空间。
//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 ));
第三个undefined
论点
第三个
undefined
参数是undefined
variables的来源。 我不确定今天是否仍然有用,但是在使用旧版浏览器/ JavaScript标准(ecmascript 5,javascript <1.8.5〜firefox 4)时,undefined
的全局范围variables是可写的,所以任何人都可以重写它的值。 第三个参数(未传递值时)创build一个名为undefined
的variables,该variables的作用域为名称空间/函数。 由于在创build名称空间时未传递值,因此缺省值为undefined
。
另一种做法,我认为这是比对象字面formsless一点限制性,是这样的:
var ns = new function() { var internalFunction = function() { }; this.publicFunction = function() { }; };
以上几乎就像模块模式 , 不pipe你喜不喜欢 ,它允许你公开所有的function,同时避免对象文字的刚性结构。
有没有更优雅或简洁的方式呢?
是。 例如:
var your_namespace = your_namespace || {};
那么你可以拥有
var your_namespace = your_namespace || {}; your_namespace.Foo = {toAlert:'test'}; your_namespace.Bar = function(arg) { alert(arg); }; with(your_namespace) { Bar(Foo.toAlert); }
我通常build立在一个封闭的:
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"; } }; })();
自从写这篇文章以来,我的风格已经有了微妙的变化,现在我发现自己写这样的封闭:
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和实现更容易理解。 将return语句看作是实现的公共接口。
因为你可能会编写不同的JavaScript文件,后来在一个应用程序中合并或不合并它们,每一个都需要能够恢复或构造命名空间对象,而不会损坏其他文件的工作。
一个文件可能打算使用命名namespace.namespace1
:
namespace = window.namespace || {}; namespace.namespace1 = namespace.namespace1 || {}; namespace.namespace1.doSomeThing = function(){}
另一个文件可能要使用命名namespace.namespace2
:
namespace = window.namespace || {}; namespace.namespace2 = namespace.namespace2 || {}; namespace.namespace2.doSomeThing = function(){}
这两个文件可以共同居住或分开而不会发生碰撞。
下面是Stoyan Stefanov在他的JavaScript Patterns书中写到的,我发现这本书非常好(它也显示了他如何评论自动生成的API文档,以及如何将一个方法添加到自定义对象的原型):
/** * 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; };
我使用这种方法:
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()
外部代码可以是:
var myClass = new myNamespace.MyClass(); var myOtherClass = new myNamepace.subName.SomeOtherClass(); myNamespace.subName.publicOtherStuff(someParameter);
这是user106826到Namespace.js的链接的后续。 这个项目似乎转移到了GitHub 。 现在是smith / namespacedotjs 。
我一直在使用这个简单的JavaScript帮助程序来处理我的小项目,到目前为止,它似乎是轻量级的,而且通用性足以处理命名空间和加载模块/类。 如果允许我将包导入到我select的名称空间中,而不仅仅是全局名称空间,那么这将是非常好的,但是除此之外,
它允许你声明命名空间,然后在该命名空间中定义对象/模块:
Namespace('my.awesome.package'); my.awesome.package.WildClass = {};
另一种select是立即声明命名空间及其内容:
Namespace('my.awesome.package', { SuperDuperClass: { saveTheDay: function() { alert('You are welcome.'); } } });
有关更多用法示例,请查看源代码中的example.js文件。
样品:
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; })()
你可以select声明一个local
variables,像self
same
,如果你希望它是私有的,可以指定local.onTimeout
。
您可以声明一个简单的函数来提供名称空间。
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!";
我创build了受Erlang模块启发的命名空间 。 这是一个非常实用的方法,但这就是我现在编写JavaScript代码的方法。
它提供了一个全局名称空间的闭包,并在该闭包中公开了一个定义的集合函数。
(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 })();
我对命名空间使用以下语法。
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/
在将我的几个库移植到不同的项目之后,不得不不断地改变顶层(静态命名的)命名空间,我已经转向使用这个小的(开源的)辅助函数来定义命名空间。
global_namespace.Define('startpad.base', function(ns) { var Other = ns.Import('startpad.other'); .... });
好处的描述在我的博客文章 。 你可以在这里获取源代码 。
我真正喜欢的好处之一就是模块之间的加载顺序隔离。 在加载之前,您可以参考一个外部模块。 当代码可用时,您获得的对象引用将被填充。
模块模式最初被定义为在传统软件工程中为类提供私有和公共封装的方式。
当使用Module模式时,我们可能会发现定义一个简单的模板是非常有用的。 这是一个涵盖名称间距,公共和私有variables。
在JavaScript中,模块模式用于进一步模拟类的概念,以便我们能够在单个对象中包含公共/私有方法和variables,从而屏蔽全局范围中的特定部分。 这样做的结果是减less了我们的函数名称与页面上其他脚本中定义的其他函数冲突的可能性。
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 ); } }; })();
优点
为什么模块模式是一个不错的select? 对于初学者来说,对于来自面向对象的开发人员来说,比起真正的封装思路,对于开发者来说,要更清洁一些,至less从JavaScript的angular度来看。
其次,它支持私有数据 – 所以在Module模式中,我们的代码的公共部分能够触及私有部分,但是外部世界无法触及这个类的私有部分。
缺点
Module模式的缺点是,我们不同地访问公共和私有成员,当我们想要改变可见性时,我们实际上必须对成员使用的每个地方进行更改。
我们也不能访问稍后添加到对象的方法中的私有成员 。 也就是说,在许多情况下,Module模式仍然是非常有用的,使用正确的话肯定有可能改进我们应用程序的结构。
揭示模块模式
现在我们对模块模式有点熟悉了,让我们来看看稍微改进的版本 – Christian Heilmann的揭示模块模式。
Heilmann感到沮丧的是,当我们想从另一个公共方法调用公共方法或访问公共variables时,他不得不重复主要对象的名称。他也不喜欢模块模式需要切换反对他希望公开的事情的文字符号。
他的努力的结果是一个更新的模式,我们将简单地定义私人范围内的所有函数和variables,并返回一个匿名对象,指向我们希望公开的私有function的指针。
下面是一个如何使用“揭示模块”模式的示例
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" );
优点
这种模式允许我们脚本的语法更加一致。 在模块的最后,我们还可以更清楚地了解我们的哪些函数和variables可以公开访问,从而简化了可读性。
缺点
这种模式的一个缺点是,如果一个私有函数引用一个公共函数,那么如果补丁是必要的,则该公共函数不能被覆盖。 这是因为私有函数将继续引用私有实现,而模式不适用于公共成员,而只适用于函数。
引用私有variables的公共对象成员也受上述无补丁规则注释的约束。
你必须检查Namespace.js !
我晚了7年,但是在这8年前做了很多工作:
- http://blogger.ziesemer.com/2008/05/javascript-namespace-function.html
- http://blogger.ziesemer.com/2007/10/respecting-javascript-global-namespace.html
能够轻松有效地创build多个嵌套的命名空间以保持复杂的Web应用程序的组织和可pipe理性,同时尊重JavaScript全局命名空间(防止命名空间污染),并且不会在命名空间path中打开任何现有对象。
从以上所述,这是我的大约2008年的解决scheme:
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; };
这不是创build一个名称空间,而是提供了一个用于创build名称空间的函数。
这可以凝结成一个缩小的单行:
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};
使用示例:
namespace("com.example.namespace"); com.example.namespace.test = function(){ alert("In namespaced function."); };
或者,作为一个声明:
namespace("com.example.namespace").test = function(){ alert("In namespaced function."); };
然后执行如下:
com.example.namespace.test();
如果您不需要传统浏览器的支持,则更新版本:
const namespace = function(name, separator, container){ var o = container || window; name.split(separator || '.').forEach(function(x){ o = o[x] = o[x] || {}; }); return o; };
现在,我只是将namespace
暴露给全局名称空间本身。 (太糟糕了,这个基础语言并没有为我们提供这个)所以我通常在闭包中使用这个,比如:
(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);
如果你需要私人范围:
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();
否则,如果你不会使用私人范围:
var yourNamespace = {}; yourNamespace.publicMethod = function() { // Do something... }; yourNamespace.publicMethod2 = function() { // Do something... }; yourNamespace.publicMethod();
我最喜欢的模式最近变得这样:
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() { // ... } })();
相当于IonuţG. Stan的回答,但是通过使用var ClassFirst = this.ClassFirst = function() {...}
来展示整洁代码的好处,它利用JavaScript的闭包范围来减less命名空间的混乱在相同的命名空间。
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()
输出:
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
我喜欢Jaco Pretorius的解决scheme,但是我想通过将它指向模块/名称空间对象来使“this”关键字更有用。 我的版本的煎锅:
(function ($, undefined) { console.log(this); }).call(window.myNamespace = window.myNamespace || {}, jQuery);
We can use it independently in this way:
var A = A|| {}; AB = {}; AB = { itemOne: null, itemTwo: null, }; ABitemOne = function () { //.. } ABitemTwo = function () { //.. }
If using a Makefile you can do this.
// 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();
I prefer to use a Makefile anyway once I get to about 1000 lines because I can effectively comment out large swaths of code by removing a single line in the makefile. It makes it easy to fiddle with stuff. Also, with this technique the namespace only appears once in the prelude so it's easy to change and you don't have to keep repeating it inside the library code.
A shell script for live development in the browser when using a makefile:
while (true); do make; sleep 1; done
Add this as a make task 'go' and you can 'make go' to keep your build updated as you code.
I've written another namespacing library that works a bit more like packages / units do in other languages. It allows you to create a package of JavaScript code and the reference that package from other code:
File hello.js
Package("hello", [], function() { function greeting() { alert("Hello World!"); } // Expose function greeting to other packages Export("greeting", greeting); });
File Example.js
Package("example", ["hello"], function(greeting) { // Greeting is available here greeting(); // Alerts: "Hello World!" });
Only the second file needs to be included in the page. Its dependencies (file hello.js in this example) will automatically be loaded and the objects exported from those dependencies will be used to populate the arguments of the callback function.
You can find the related project in Packages JS .
My habit is to use function myName() as property storage, and then var myName as "method" holder…
Whether this is legitimate enough or not, beat me! I am relying on my PHP logic all the time, and things simply work. :d
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();
You can also do it in a 'vice versa' way to check before object creation which is much better :
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();
Reference to this: JavaScript: Creating Object with Object.create()
In javascript there are no pre defined methods to use namespaces. In JavaScript we have to create our own methods to define NameSpaces. Here is a procedure we follow in Oodles technologies.
Register a NameSpace Following is the function to register a name space
//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]]; } }
To register a Namespace just call the above function with the argument as name space separated by .(dot). For Example Let your application name is oodles. You can make a namespace by following method
[R
egisterNS("oodles.HomeUtilities"); registerNS("oodles.GlobalUtilities"); var $OHU = oodles.HomeUtilities; var $OGU = oodles.GlobalUtilities;
Basically it will create your NameSpaces structure like below in backend:
var oodles = { "HomeUtilities": {}, "GlobalUtilities": {} };
In the above function you have register a namespace calles "oodles.HomeUtilities" and "oodles.GlobalUtilities". To call these namespaces we make an variable ie var $OHU and var $OGU.
These variables are nothing but an alias to Intializing the namespace. Now, Whenever you declare a function that belong to HomeUtilities you will declare it like following:
$OHU.initialization = function(){ //Your Code Here };
Above is the function name initialization and it is put into an namespace $OHU. and to call this function anywhere in the script files. Just use following code.
$OHU.initialization();
Similarly, with the another NameSpaces.
希望它有帮助。