如何在JavaScript中创建命名空间,以便我的对象和函数不会被其他同名的对象和函数覆盖?我用过以下内容:
if (Foo == null || typeof(Foo) != "object") { var Foo = new Object();}
这样做有更优雅或简洁的方式吗?
我使用Enterprise jQuery站点上的方法:
以下是他们展示如何声明私有和公共属性和函数的示例.一切都是作为一个自动执行的匿名函数完成的.
(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
.我不确定它今天是否仍然有用,但是在使用旧的浏览器/ JavaScript标准(ecmascript 5,javascript <1.8.5~firefox 4)时,全局范围变量undefined
是可写的,所以任何人都可以重写它的值.第三个参数(未传递值时)创建一个名为undefined
range 的变量,该变量的作用域为命名空间/函数.由于在创建名称空间时未传递任何值,因此默认为该值undefined
.
我喜欢这个:
var yourNamespace = { foo: function() { }, bar: function() { } }; ... yourNamespace.foo();
另一种方法,我认为它比对象文字形式稍微限制一点,是这样的:
var ns = new function() { var internalFunction = function() { }; this.publicFunction = 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); }
我通常在一个闭包中构建它:
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助手,到目前为止,它看起来很轻但功能多,足以处理命名空间和加载模块/类.如果它允许我将一个包导入我选择的命名空间,而不仅仅是全局命名空间......叹息,那将是很棒的,但除此之外.
它允许您声明命名空间,然后在该命名空间中定义对象/模块:
Namespace('my.awesome.package'); my.awesome.package.WildClass = {};
另一种选择是立即声明命名空间及其内容:
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; })()
您可以选择声明一个local
变量,same
像self
和分配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!";
如果您需要私人范围:
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();
模块模式最初被定义为为传统软件工程中的类提供私有和公共封装的方法.
使用Module模式时,我们可能会发现定义一个我们用于开始使用它的简单模板很有用.这是一个涵盖名称间距,公共和私有变量的.
在JavaScript中,Module模式用于进一步模拟类的概念,使得我们能够在单个对象中包含公共/私有方法和变量,从而将特定部分与全局范围隔离开来.这导致我们的函数名称与页面上其他脚本中定义的其他函数冲突的可能性降低.
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 ); } }; })();
好处
为什么Module模式是个不错的选择?对于初学者来说,对于来自面向对象背景的开发人员来说,比真正封装的想法更加清晰,至少从JavaScript的角度来看.
其次,它支持私有数据 - 因此,在模块模式中,代码的公共部分能够触及私有部分,但外部世界无法触及类的私有部分.
缺点
Module模式的缺点是,当我们以不同方式访问公共成员和私有成员时,当我们希望更改可见性时,我们实际上必须对使用该成员的每个位置进行更改.
我们也无法在以后添加到对象的方法中访问私有成员.也就是说,在许多情况下,模块模式仍然非常有用,如果使用得当,肯定有可能改善我们的应用程序结构.
揭示模块模式
现在我们对模块模式稍微熟悉一下,让我们看一下稍微改进的版本 - Christian Heilmann的Revealing Module模式.
揭示模块模式的出现是因为Heilmann对于当我们想要从另一个公共方法调用一个公共方法或访问公共变量时必须重复主对象的名称这一事实感到沮丧.他也不喜欢Module模式的必须切换的要求对他希望公开的事物反对文字符号.
他努力的结果是一个更新的模式,我们将简单地定义私有范围中的所有函数和变量,并返回一个匿名对象,其中包含指向我们希望公开的私有功能的指针.
下面是一个如何使用Revealing Module模式的例子
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" );
好处
此模式允许脚本的语法更加一致.它还使模块结尾处的内容更加清晰,我们可以公开访问哪些函数和变量,从而简化了可读性.
缺点
这种模式的缺点是,如果私有函数引用公共函数,则如果需要补丁,则不能覆盖该公共函数.这是因为私有函数将继续引用私有实现,并且该模式不适用于公共成员,仅适用于函数.
引用私有变量的公共对象成员也受上面的无补丁规则说明的约束.
我创建的命名空间受到Erlang模块的启发.这是一种非常实用的方法,但这就是我最近编写JavaScript代码的方法.
它为闭包提供了一个全局命名空间,并在该闭包中公开了一个已定义的set函数.
(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'); .... });
这些好处的描述在我的博客文章中.您可以在此处获取源代码.
我真正喜欢的一个好处是模块之间的负载顺序隔离.您可以在加载外部模块之前参考它.当代码可用时,您将获得对象引用.
我参加晚会已经晚了7年,但是在8年前做了相当多的工作:
http://blogger.ziesemer.com/2008/05/javascript-namespace-function.html
http://blogger.ziesemer.com/2007/10/respecting-javascript-global-namespace.html
重要的是能够轻松高效地创建多个嵌套命名空间,以保持复杂的Web应用程序的组织和可管理性,同时尊重JavaScript全局命名空间(防止命名空间污染),同时不破坏命名空间路径中的任何现有对象.
从上面来看,这是我的大约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; };
这不是创建命名空间,而是提供创建命名空间的功能.
这可以浓缩为缩小的单行:
var namespace=function(c,f,b){var e=c.split(f||"."),g=b||window,d,a;for(d=0,a=e.length;d使用示例:
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);