本文共 17652 字,大约阅读时间需要 58 分钟。
如何在JavaScript中创建名称空间,以使我的对象和函数不会被其他同名对象和函数覆盖? 我使用了以下内容:
if (Foo == null || typeof(Foo) != "object") { var Foo = new Object();}
有没有更优雅或更简洁的方法?
这是Stoyan Stefanov在他的书中做到这一点的方法,我发现它非常好(它还显示了他如何做注释以允许自动生成的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;};
您可以声明一个简单的函数来提供名称空间。
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 examplenamespace("foo.bar").baz = "I'm a value!";
样品:
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
如果你希望它是私有的。
在将我的几个库移植到不同的项目之后,并且不得不不断地更改顶级(静态命名)名称空间之后,我切换到使用这个小的(开源)帮助函数来定义名称空间。
global_namespace.Define('startpad.base', function(ns) { var Other = ns.Import('startpad.other'); ....});
有关好处的说明,请参阅我的 。 您可以在获取 。
我真正喜欢的好处之一是模块之间就装入顺序而言是隔离的。 您可以在加载外部模块之前参考它。 当代码可用时,将填写您获得的对象引用。
我喜欢Jaco Pretorius的解决方案,但我想通过将“ this”关键字指向模块/命名空间对象来使其更加有用。 我的煎锅版本:
(function ($, undefined) { console.log(this);}).call(window.myNamespace = window.myNamespace || {}, jQuery);
我对命名空间使用以下语法。
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: :
有没有更优雅或更简洁的方法?
是。 例如:
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);}
我的习惯是使用函数myName()作为属性存储,然后使用var myName作为“方法”持有人...
不管这合法与否,都击败我! 我一直都在依赖我的PHP逻辑,并且一切正常。 :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();
您还可以“反之亦然”的方式进行操作,以在创建对象之前进行检查,这要好得多 :
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();
对此的引用:
很好地跟进了Ionu'G. Stan的答案,但是通过使用var ClassFirst = this.ClassFirst = function() {...}
展示了整洁的代码的好处,该方法利用了JavaScript的闭包作用域,从而减少了类的名称空间混乱在相同的名称空间中。
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: 123Nicer way to access a class in same namespace: 123Cluttered way to access another class in namespace: 666Nicer way to access a class in same namespace: 666
我创建了受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 A = A|| {};A.B = {};A.B = { itemOne: null, itemTwo: null,};A.B.itemOne = function () { //..}A.B.itemTwo = function () { //..}
我最近最喜欢的模式是:
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。
在这种情况下使用函数表达式的模式导致无法在不检查整个代码的情况下就知道公开哪些方法。
这是user106826到Namespace.js链接的后续操作。 看来该项目已移至 。 现在是 。
我一直在为我的小型项目使用此简单的JavaScript帮助程序,到目前为止,它似乎很轻巧,但功能强大,足以处理命名空间和加载模块/类。 如果它允许我将包导入到我选择的命名空间中,那不仅会是全局命名空间,而且还不错。
它允许您声明名称空间,然后在该名称空间中定义对象/模块:
Namespace('my.awesome.package');my.awesome.package.WildClass = {};
另一个选择是立即声明名称空间及其内容:
Namespace('my.awesome.package', { SuperDuperClass: { saveTheDay: function() { alert('You are welcome.'); } }});
有关更多用法示例,请查看中的example.js文件。
在JavaScript中,没有预定义的方法可以使用名称空间。 在JavaScript中,我们必须创建自己的方法来定义NameSpaces。 这是我们在Oodles技术中遵循的过程。
注册名称空间以下是注册名称空间的功能
//Register NameSpaces Functionfunction 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]]; }}
要注册名称空间,只需调用上述函数,并将参数作为名称空间,并用'.'
分隔即可'.'
(点)。 例如,让您的应用程序名称为oodles。 您可以通过以下方法创建名称空间
registerNS("oodles.HomeUtilities");registerNS("oodles.GlobalUtilities");var $OHU = oodles.HomeUtilities;var $OGU = oodles.GlobalUtilities;
基本上,它将在后端创建如下所示的NameSpaces结构:
var oodles = { "HomeUtilities": {}, "GlobalUtilities": {}};
在上面的函数中,您注册了一个名为"oodles.HomeUtilities"
和"oodles.GlobalUtilities"
的命名空间。 要调用这些命名空间,我们创建一个变量,即var $OHU
和var $OGU
。
这些变量不过是初始化名称空间的别名。 现在,每当您声明一个属于HomeUtilities
的函数时,都将像下面这样声明它:
$OHU.initialization = function(){ //Your Code Here};
上面是函数名称初始化,并将其放入命名空间$OHU
。 并在脚本文件中的任何位置调用此函数。 只需使用以下代码。
$OHU.initialization();
同样,与另一个NameSpaces。
希望能帮助到你。
我晚了7年,但是在这8年前做了很多工作:
重要的是,能够轻松高效地创建多个嵌套的名称空间,以保持复杂的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);
在较大的应用程序中,仅在页面加载开始时定义一次(对于基于客户端的Web应用程序)。 如果保留,其他文件则可以重用名称空间功能(在上面作为“可选”提供)。 最糟糕的是,如果多次声明此函数-只需几行代码,如果缩小则更少。
因为您可能编写了不同的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(){}
这两个文件可以一起生活或生活在一起而不会发生冲突。
模块模式最初被定义为在常规软件工程中为类提供私有和公共封装的一种方式。
当使用模块模式时,我们可能会发现定义一个简单的模板以用于入门非常有用。 这是一个涵盖名称空间,公共和私有变量的文件。
在JavaScript中,模块模式用于进一步模拟类的概念,以使我们能够在单个对象中包含公共/私有方法和变量,从而使特定部分不受全局范围的影响。 这样可以减少我们的函数名与页面上其他脚本中定义的其他函数发生冲突的可能性。
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的Revealing Module模式。
揭露模块模式的出现是因为Heilmann感到沮丧的是,当我们想从另一个公共方法调用另一个公共方法或访问公共变量时,他不得不重复主对象的名称。他也不喜欢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" );
好处
这种模式可以使脚本的语法更加一致。 它还使在模块末尾可以更清楚地知道我们的哪些函数和变量可以公开访问,从而简化了可读性。
缺点
这种模式的缺点是,如果私有函数引用了公共函数,则在需要补丁的情况下不能覆盖该公共函数。 这是因为私有函数将继续引用私有实现,并且该模式不适用于公共成员,而仅适用于函数。
引用私有变量的公共对象成员也应遵守上面的无补丁规则说明。
如果需要私有范围:
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();
我编写了另一个命名空间库,该库的工作方式与其他语言中的包/单元更像。 它允许您创建一个JavaScript代码包,并创建来自其他代码的引用:
Package("hello", [], function() { function greeting() { alert("Hello World!"); } // Expose function greeting to other packages Export("greeting", greeting);});
Package("example", ["hello"], function(greeting) { // Greeting is available here greeting(); // Alerts: "Hello World!"});
页面中仅需要包含第二个文件。 它的依赖项(在此示例中为hello.js文件)将自动加载,从这些依赖项导出的对象将用于填充回调函数的参数。
您可以在找到相关项目。
我想你们都为这样一个简单的问题使用了太多的代码。 无需为此进行回购。 这是一个单行函数。
namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window);
试试吧 :
// --- definition --- const namespace = namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window); // --- Use ---- let myNamespace = namespace("abc"); myNamespace.MyClass = class MyClass {}; // --- see ---- console.log("a : ", a);
如果使用Makefile,则可以执行此操作。
// prelude.hjsbilly = new ( function moduleWrapper () { const exports = this;// postlude.hjsreturn exports;})();// someinternalfile.jsfunction bob () { console.log('hi'); }exports.bob = bob;// clientfile.jsbilly.bob();
每当我达到大约1000行时,我还是更喜欢使用Makefile,因为我可以通过删除makefile中的一行来有效地注释掉一大堆代码。 它使弄弄东西变得容易。 同样,使用这种技术,名称空间仅在序言中出现一次,因此更改很容易,而且您不必在库代码中重复重复它。
使用makefile时在浏览器中进行实时开发的shell脚本:
while (true); do make; sleep 1; done
将其添加为make任务“执行”,您可以“ make go”在编写代码时使构建保持更新。
我使用 :
这是他们的示例,显示了如何声明私有和公共属性和功能。 一切都作为一个自执行的匿名函数完成。
(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
的变量,其作用域为名称空间/函数。 因为在创建名称空间时没有传递任何值,所以它默认为undefined
值。
我通常将其构建在一个闭包中:
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语句视为实现的公共接口。
我喜欢这个:
var yourNamespace = { foo: function() { }, bar: function() { }};...yourNamespace.foo();
我使用这种方法:
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 filemyNamespace.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);
我认为它的限制比对象文字形式的限制少一点,它是这样的:
var ns = new function() { var internalFunction = function() { }; this.publicFunction = function() { };};
上面的代码非常类似于 , ,它都可以将所有函数公开为公共,同时避免对象文字的僵化结构。
转载地址:http://caogj.baihongyu.com/