Home >Web Front-end >JS Tutorial >Problem description on Javascript modularization and namespace management_javascript skills

Problem description on Javascript modularization and namespace management_javascript skills

WBOY
WBOYOriginal
2016-05-16 18:14:241008browse

[About modularization and why we need modularization]

Let’s first talk about why we need modularization. In fact, this is still related to the convenience of coding ideas and code management (the problem of namespace pollution is not mentioned because I believe that coders who have considered modular ideas should at least have their own set of naming rules. In small and medium-sized projects, In the site, the probability of namespace pollution is very small, but it does not mean that it does not exist. This issue will be discussed later).
In fact, the modular idea is still the same as the object-oriented idea, except that the so-called "module" in our mouth may be a larger object than the so-called "object". We combine functional functions that are dedicated to accomplishing the same purpose through good encapsulation and ensure good reusability. We can probably call the idea of ​​such a combined code fragment an object-oriented idea. There are many benefits to doing this, such as: ease of use, versatility, maintainability, readability, avoiding variable name pollution, etc.
Modularization is nothing more than object-oriented and module-oriented. We organically combine functional packages related to the same project (module) and manage them through a common name. It can be roughly said to be a modular idea. Therefore, compared to object-oriented, I think it is actually easier to implement the modular idea in code architecture than object-oriented.
Unlike c#, java and other strongly typed languages ​​that have good modularity and namespace mechanisms. JavaScript does not provide any language features for creating and managing modules. Because of this, when we are doing js coding, the use of so-called namespaces will seem a bit too casual (myself included). For example:

Copy code The code is as follows:

var Hongru = {} // namespace

(function(){
Hongru.Class1 = function () {
//TODO
}
...
Hongru.Class2 = function () {
//TODO
}
})();


As above, we usually use a global variable or global object as our namespace. It is so simple that it even seems a bit casual to entrust it with such a major responsibility. But can we say this is bad? No, on the contrary, I think students who have this kind of coding habit should be praised. . .


So, when we are doing some projects or building some small-scale websites, it is actually enough to simply use this method to do namespace work, and basically there will be no major problems. trouble. But back to the essence, if you are obsessed with code cleanliness or building a large-scale website, or if you approach the code structure with an absolutely elegant attitude and logic from the beginning. Perhaps we should consider better namespace registration and management methods.
In this aspect, jQuery is slightly inferior compared to YUI, Mootool, EXT, etc. (although jq also has its own set of modular mechanisms), but this still does not hinder our love for it. After all, it is sideways. The focus is different. The strength of jq lies in its selector, otherwise it wouldn't be called j-Query.
So it makes sense for us to say that jQuery is more suitable for small and medium-sized websites. Just like Douban's open source front-end lightweight framework Do framework, it is also built on jQuery, encapsulating a layer of modular management ideas and the function of synchronous file loading.

[About namespace]

Okay, let’s get back to the topic. In the above way, simply using global objects to create namespace can effectively reduce global variables and avoid the problem of variable name pollution. , but once the website becomes larger or there are many projects, there will still be problems in managing the name spaces of multiple global objects. If by chance a name conflict occurs, one module will overwrite the properties of another module, causing one or both to not work properly. And when a problem occurs, it is quite troublesome to identify it. So we may need a mechanism or tool that can determine whether there are duplicate names when creating a namespace.

On the other hand, different modules, that is, different namespaces, are not completely independent. Sometimes we also need to create the same method or attribute in different namespaces. In this case, the import and Exporting can also be a problem.

Regarding the above two aspects, I thought about it a little and did some tests, but there are still some mistakes. Today I read the "Rhinoceros Book" again. It is indeed a classic. It easily solved the above problems. Based on the solution and demo of "Rhinoceros Book", I made some modifications and simplifications. Share your general understanding. The more important points are the following:

--Test the usability of each sub-module

Since our namespace is an object, it has the hierarchical relationship that the object should have, so during detection The availability of a namespace needs to be judged and registered based on such a hierarchical relationship. This is especially important when registering a sub-namespace. For example, we have registered a new namespace as Hongru, and then we need to register another namespace as Hongru.me. That is to say, our original intention is that the me namespace is a sub-namespace of Hongru, and they should have a father-son relationship. Therefore, when registering a namespace, you need to split it through ‘.’ and make corresponding judgments one by one. Therefore, the code to register a namespace is roughly as follows:

Copy code The code is as follows:

// create namespace --> return a top namespace
Module.createNamespace = function (name, version) {
if (!name) throw new Error('name required');
if (name.charAt(0) == '.' || name.charAt(name.length-1) == '.' || name.indexOf('..') != -1) throw new Error(' illegal name');

var parts = name.split('.');

var container = Module.globalNamespace;
for (var i=0; ivar part = parts[i];
if (!container[part]) container[part] = {};
container = container[part];
}

var namespace = container;
if (namespace.NAME) throw new Error('module "' name '" is already defined');
namespace.NAME = name;
if ( version) namespace.VERSION = version;

Module.modules[name] = namespace;
return namespace;
};

Note: The above Module is our A common Module to register and manage namespace. As a "base module", it has a module queue attribute of modules, which is used to store our newly registered namespace. Because of this queue, we can easily determine the namespace. has been registered:


Copy code The code is as follows:

var Module;
//check Module --> make sure 'Module' is not existed
if (!!Module && (typeof Module != 'object' || Module.NAME )) throw new Error("NameSpace 'Module' already Exists!");

Module = {};

Module.NAME = 'Module';
Module.VERSION = 0.1 ;

Module.EXPORT = ['require',
'importSymbols'];

Module.EXPORT_OK = ['createNamespace',
'isDefined',
' modules',
'globalNamespace'];

Module.globalNamespace = this;

Module.modules = {'Module': Module};

The last line of the above code is a namespace queue, and all newly created namespaces will be placed in it. Combined with the previous piece of code, we can basically manage our namespace very well. As for the "base module" Module, it also has other attributes such as EXPORT, which will be discussed later. The following is a test demo for creating a namespace

Copy the code The code is as follows:

Module.createNamespace('Hongru', 0.1);//Register a namespace named Hongru, version 0.1

The second version parameter above can also be omitted, if you do not need a version number . You can see our newly registered namespace under chrome-debugger


You can see that the newly registered Hongru namespace has taken effect: Look at the module queue of Module:

You can find that the newly registered Hongru has also added Module in the modules queue. Everyone has also discovered that there are several methods such as require, isDefined, and importSymbols in Module.
Since these two methods require (detecting version) and isDefined (already registered when detecting namespace) are not difficult, they are a little simpler:

--Version and duplicate name detection


Copy code The code is as follows:

// check name is defined or not
Module.isDefined = function (name) {
return name in Module.modules;
} ;
// check version
Module.require = function (name, version) {
if (!(name in Module.modules)) throw new Error('Module ' name ' is not defined') ;
if (!version) return;
var n = Module.modules[name];
if (!n.VERSION || n.VERSION < version) throw new Error('version ' version ' or greater is required');
};

The above two methods are very simple. I believe everyone understands that one is to detect whether the queue has the same name, and the other is to detect whether the version meets the requirements. version. There is nothing special about it, so I won’t go into details. What is a little more complicated is the mutual import of attributes or methods between name spaces.
--Export of attributes or methods of tags in namespaces
Since what we want is a universal namespace registration and management tool, we need to consider configurability when importing or exporting tags. You cannot import or export them all at once. So we have the two Arrays EXPORT and EXPORT_OK in the Module template we see as tag queues to store the attributes or methods we are allowed to export. Among them, EXPORT is the public tag queue, and EXPORT_OK is the tag queue that we can customize. If you think it is not so clear, you can also use only one tag queue to store the tag attributes or methods that you are allowed to export.
With the tag queue, the export operation we do is only for the tags in the EXPORT and EXPORT_OK tag queues.
Copy code The code is as follows:

// import module
Module.importSymbols = function (from) {
if (typeof form == 'string') from = Module.modules[from];
var to = Module.globalNamespace; //dafault
var symbols = [];
var firstsymbol = 1;
if (arguments.length>1 && typeof arguments[1] == 'object' && arguments[1] != null) {
to = arguments[1];
firstsymbol = 2;
}
for (var a=firstsymbol; asymbols.push(arguments[a]);
}
if (symbols.length == 0) {
//default export list
if (from.EXPORT) {
for (var i=0; ivar s = from.EXPORT[i];
to[s] = from[s];
}
return;
} else if (!from.EXPORT_OK) {
// EXPORT array && EXPORT_OK array both undefined
for (var s in from) {
to[s] = from[s];
return;
}
}
}
if (symbols.length > 0) {
var allowed;
if (from.EXPORT || form.EXPORT_OK) {
allowed = {};
if (from.EXPORT) {
for (var i=0; iallowed[from.EXPORT[i]] = true;
}
}
if (from.EXPORT_OK) {
for (var i=0; iallowed[form.EXPORT_OK[i]] = true;
}
}
}
}
//import the symbols
for (var i=0; ivar s = symbols[i];
if (!(s in from)) throw new Error('symbol ' s ' is not defined');
if (!!allowed && !(s in allowed)) throw new Error(s ' is not public, cannot be imported');
to[s] = form[s];
}
}

这个方法中第一个参数为导出源空间,第二个参数为导入目的空间(可选,默认是定义的globalNamespace),后面的参数也是可选,为你想导出的具体属性或方法,默认是标记队列里的全部。
下面是测试demo:
复制代码 代码如下:

Module.createNamespace('Hongru');
Module.createNamespace('me', 0.1);
me.EXPORT = ['define']
me.define = function () {
this.NAME = '__me';
}
Module.importSymbols(me, Hongru);//把me名字空间下的标记导入到Hongru名字空间下

可以看到测试结果:
 

 

 本来定义在me名字空间下的方法define()就被导入到Hongru名字空间下了。当然,这里说的导入导出,其实只是copy,在me名字空间下依然能访问和使用define()方法。

好了,大概就说到这儿吧,这个demo也只是提供一种管理名字空间的思路,肯定有更加完善的方法,可以参考下YUI,EXT等框架。或者参考《JavaScript权威指南》中模块和名字空间那节。

最后贴下源码:

复制代码 代码如下:

/* == Module and NameSpace tool-func ==
* author : hongru.chen
* date : 2010-12-05
*/
var Module;
//check Module --> make sure 'Module' is not existed
if (!!Module && (typeof Module != 'object' || Module.NAME)) throw new Error("NameSpace 'Module' already Exists!");
Module = {};
Module.NAME = 'Module';
Module.VERSION = 0.1;
Module.EXPORT = ['require',
'importSymbols'];
Module.EXPORT_OK = ['createNamespace',
'isDefined',
'modules',
'globalNamespace'];
Module.globalNamespace = this;
Module.modules = {'Module': Module};
// create namespace --> return a top namespace
Module.createNamespace = function (name, version) {
if (!name) throw new Error('name required');
if (name.charAt(0) == '.' || name.charAt(name.length-1) == '.' || name.indexOf('..') != -1) throw new Error('illegal name');
var parts = name.split('.');
var container = Module.globalNamespace;
for (var i=0; ivar part = parts[i];
if (!container[part]) container[part] = {};
container = container[part];
}
var namespace = container;
if (namespace.NAME) throw new Error('module "' name '" is already defined');
namespace.NAME = name;
if (version) namespace.VERSION = version;
Module.modules[name] = namespace;
return namespace;
};
// check name is defined or not
Module.isDefined = function (name) {
return name in Module.modules;
};
// check version
Module.require = function (name, version) {
if (!(name in Module.modules)) throw new Error('Module ' name ' is not defined');
if (!version) return;
var n = Module.modules[name];
if (!n.VERSION || n.VERSION < version) throw new Error('version ' version ' or greater is required');
};
// import module
Module.importSymbols = function (from) {
if (typeof form == 'string') from = Module.modules[from];
var to = Module.globalNamespace; //dafault
var symbols = [];
var firstsymbol = 1;
if (arguments.length>1 && typeof arguments[1] == 'object' && arguments[1] != null) {
to = arguments[1];
firstsymbol = 2;
}
for (var a=firstsymbol; asymbols.push(arguments[a]);
}
if (symbols.length == 0) {
//default export list
if (from.EXPORT) {
for (var i=0; ivar s = from.EXPORT[i];
to[s] = from[s];
}
return;
} else if (!from.EXPORT_OK) {
// EXPORT array && EXPORT_OK array both undefined
for (var s in from) {
to[s] = from[s];
return;
}
}
}
if (symbols.length > 0) {
var allowed;
if (from.EXPORT || form.EXPORT_OK) {
allowed = {};
if (from.EXPORT) {
for (var i=0; iallowed[from.EXPORT[i]] = true;
}
}
if (from.EXPORT_OK) {
for (var i=0; iallowed[form.EXPORT_OK[i]] = true;
}
}
}
}
//import the symbols
for (var i=0; ivar s = symbols[i];
if (!(s in from)) throw new Error('symbol ' s ' is not defined');
if (!!allowed && !(s in allowed)) throw new Error(s ' is not public, cannot be imported');
to[s] = form[s];
}
}
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn