模块模式:
模块模式是用来封装逻辑并避免命名空间污染的好方法。使用匿名函数可以做到这一点,通常是创建一个匿名函数并立即执行它。在匿名函数中的逻辑都在闭包里运行,为应用中的变量提供了局部作用域和私有的运行环境:
(function(){
/* ... */
})();
在执行这个匿名函数之前,我们用一对括号()将它包起来。这样才能让javascirpt解释器正确地将这段代码解释为一个语句。
全局导入:
定义在模块里的变量都是局部变量,因此在全局命名空间中是无法访问他们的。然而应用的全局变量仍是可用的,从模块的内部可用很容易的访问并操纵它们。开发者往往很难一眼看出哪个全局变量被模块使用了,尤其是当模块数量很多的时候。
另外,隐式的全局变量会让程序变的更慢,因为javascript解释器不得不遍历作用域链。
将全局对象作为参数传入匿名函数,可以导入我们的代码中,这种实现比隐式的全局对象更加简洁高效。
(function($){
/* ...*/
})(jQuery);
这个例子我们将全局对象jQuery导入我们的模块里,并将其重命名$。它清晰的表明这个模块中所用到的全局变量是什么,并且对这个全局对象的读取速度更快。
全局导出:
(function($,exports){
exports.Foo="wem";
})(jQuery,window);
assertEqual(Foo,"wem");
这里我们使用的变量名叫exports,用它来暴露全局变量,这样代码看起来干净易读,可以直接看出模块创建了哪些全局变量。
添加少量上下文
使用局部上下文是一种架构模块很有用的方法,特别是当需要给事件注册回调函数时。实际情况是,模块中的上下文都是全局的,this就是window
(function(){
assertEqual(this,window);
})();
如果想自定义作用域的上下文,则需要将函数添加至一个对象中,比如:
(funcion(){
var mod={};
mod.contextFunction=function(){
assertEqual(this,mod);
};
mod.contextFunction();
})();
在 mod.contextFunction()中上下文不是全局的,而是mod对象。这时使用this就不用担心创建全局变量了。
(funcion($){
var mod={};
mod.load=function(func){
$($.proxy(func,this));
};
mod.load(function(){
this.view=$("#view");
});
mod.assetsClick=function(){
//处理点击
};
mod.load(funtion(){
this.view.find(".assets").click($.proxy(this.assetsClick,this));
});
})(jQuery);
这里创建了新的load()函数来处理回调,当页面加载后执行它,我们使用了jQuery的proxy()来确保回调函数是基于正确的上下文执行的
抽象出类库
(function($,exports){
var mod=function(includes){
if(includes)this.include(includes);
};
mod.fn=mod.prototype;
mod.fn.proxy=function(func){
return $.proxy(func,this);
};
mod.fn.load=function(func){
$(this.proxy(func));
};
mod.fn.include=function(ob){
$.extend(this,ob);
};
exports.Controller=mod;
})(jQuery,window);
proxy()保证了函数在局部上下文中执行,对于事件回调来说是非常有用的模式。
include()函数只是给控制器添加属性、保存类型功能的快捷方式。
代码中将控制器挂载到export对象中,对外暴露为全局的Controller变量
例子:根据鼠标是否移过元素来给它添加和删除一个元素的类
(function($,Controller){
var mod=new Controller;
mod.toggleClass=function(){
this.view.toggleClass("over",e.data)
};
mod.load(function(){
this.view=$("#view");
this.view.mouseover(this.proxy(this.toggleClass),true);
this.view.mouseout(this.proxy(this.toggleClass),false);
}
);
})(jQuery,Controller)
文档加载完后载入控制器
//使用全局对象作为上下文而不是window对象
//用来创建全局对象
var exports=this;
(function(){
var mod={};
mod.create=function(includes){
var result=function(){
this.init.apply(this,argument);
};
result.fn=result.prototype;
result.fn.init=function(){};
result.proxy=function(func){return $.proxy(func,this);};
result.fn.proxy=result.proxy;
result.include=function(obj){$.extend(result.fn,obj);};
result.extend=function(obj){$.extend(result,obj);};
if(includes)result.include(includes);
return result;
};
exports.Controller=mod;
})(jQuery);
现在我们可以使用新的Controller.create()函数来创建控制器,传入一个包含实例属性的对象直接量
注意这里整个控制器都被包装在jQuery(function(){/* ... */})中,这是jQuery.ready()的另一种写法,
这句话的意思就是在页面DOM节点构建完成后才执行初始化的动作加载控制器。
jQuery(function($){
var ToggleView = Controller.create({
init: function(view){
this.view = $(view);
this.view.mouseover(this.proxy(this.toggleClass), true);
this.view.mouseout(this.proxy(this.toggleClass), false);
},
this.toggleClass: function(e){
this.view.toggleClass("over", e.data);
}
});
// Instantiate controller, calling init()
new ToggleView("#view");
});