本文解析JavaScript中类似Sea.js和Require.js的模块化机制,通过简单代码演示define和module的实现。核心在于利用对象属性挂载方法,并通过闭包保护内部状态,防止外部直接访问,从而构建基础的模块依赖管理模型。
JavaScript模块化基础与实现原理
在使用Sea.js、Require.js或Angular等框架时,
我们通常采用define和module(或require)的方式来定义模块,并处理模块间的依赖关系。
下面给出define和module的简单实现代码。其本质逻辑,就是向一个对象添加相应的方法。
var F = {};
F.define = function(str,fn){
var parts = str.split(".");
var parent = this; // parent 当前模块的父模块
var i = len = 0;
//如果第一个模块是模块单体对象,则移除
if(parts[0] == "F"){
parts = parts.slice(1);
}
//屏蔽对 define module 模块方法重写
if(parts[0] == "define" || parts[0] == "module"){
return ;
}
for(len = parts.length; i < len-1; i++){
//如果父模块中不存在当前模块
if(typeof parent[parts[i]] === 'undefined'){
//声明当前模块
parent[parts[i]] = {};
}
//缓存下一层父模块
parent = parent[parts[i]];
}
if(fn && parts[i]){
//如果给定模块方法,则绑定该模块的方法,
parent[parts[i]] = fn();
}
return this;
}
F.module = function(){
var args = [].slice.call(arguments);//复制参数
var fn = args.pop(); //获取回调
var parts = args[0] && args[0] instanceof Array ? args[0] : args;
//模块的依赖
var modules = [];
//模块的路由
var modIDs = "";
//依赖模块的索引
var i = 0;
var len = parts.length; // 依赖模块的长度
var parent,j,jlen; //父级模块,模块路由层级索引,模块路由层级长度
while(i < len){
if(typeof parts[i] == "string"){
parent = this;
//解析路由,并且屏蔽掉 F
modIDs = parts[i].replace(/^F\./,"").split(".");
//遍历模块层级
for( j = 0,jlen = modIDs.length; j < jlen; j++){
//迭代 父模块
parent = parent[modIDs[j]] || false;
}
modules.push(parent); //将模块添加到依赖列表
}else{
//直接将模块添加到依赖列表
modules.push(parts[i]);
}
//取下一个模块
i++;
}
//执行回调,将依赖的模块注入
fn.apply(null,modules);
}
//定义 string 模块
F.define("string",function(){
return {
trim(str){
return str.replace(/^s+|\s+$/g,"");
}
}
});
//定义 string 模块,的子模块 sub
F.define("string.sub",function(){
return {
low(str){
return str.toLowerCase();
}
}
});
console.log(F);
//使用模块
F.module(["string","string.sub",document],function(str,strSub,doc){
console.log(str,strSub,doc)
});
当然,上述示例中使用的F对象在实际应用中应当封装在闭包内,以避免外界直接访问。基于此需求,代码如下所示。
var Sea = (function(){
var F = {};
F.define = function(str,fn){
var parts = str.split(".");
var parent = this; // parent 当前模块的父模块
var i = len = 0;
//如果第一个模块是模块单体对象,则移除
if(parts[0] == "F"){
parts = parts.slice(1);
}
//屏蔽对 define module 模块方法重写
if(parts[0] == "define" || parts[0] == "module"){
return ;
}
for(len = parts.length; i < len-1; i++){
//如果父模块中不存在当前模块
if(typeof parent[parts[i]] === 'undefined'){
//声明当前模块
parent[parts[i]] = {};
}
//缓存下一层父模块
parent = parent[parts[i]];
}
if(fn && parts[i]){
//如果给定模块方法,则绑定该模块的方法,
parent[parts[i]] = fn();
}
return this;
}
F.module = function(){
var args = [].slice.call(arguments);//复制参数
var fn = args.pop(); //获取回调
var parts = args[0] && args[0] instanceof Array ? args[0] : args;
//模块的依赖
var modules = [];
//模块的路由
var modIDs = "";
//依赖模块的索引
var i = 0;
var len = parts.length; // 依赖模块的长度
var parent,j,jlen; //父级模块,模块路由层级索引,模块路由层级长度
while(i < len){
if(typeof parts[i] == "string"){
parent = this;
//解析路由,并且屏蔽掉 F
modIDs = parts[i].replace(/^F\./,"").split(".");
//遍历模块层级
for( j = 0,jlen = modIDs.length; j < jlen; j++){
//迭代 父模块
parent = parent[modIDs[j]] || false;
}
modules.push(parent); //将模块添加到依赖列表
}else{
//直接将模块添加到依赖列表
modules.push(parts[i]);
}
//取下一个模块
i++;
}
//执行回调,将依赖的模块注入
fn.apply(null,modules);
}
return {
define:function(){
F.define.apply(F,arguments);
},
module:function(){
F.module.apply(F,arguments);
}
}
})();
//定义 string 模块
Sea.define("string",function(){
return {
trim(str){
return str.replace(/^s+|\s+$/g,"");
}
}
});
//定义 string 模块,的子模块 sub
Sea.define("string.sub",function(){
return {
low(str){
return str.toLowerCase();
}
}
});
console.log(Sea);
//使用模块
Sea.module(["string","string.sub",document],function(str,strSub,doc){
console.log(str,strSub,doc)
});
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。