本文整理自MVC的JavaScript Web富應(yīng)用開發(fā),歡迎購(gòu)買原版書籍
一、MVC是一種設(shè)計(jì)模式
4、類的概念
JavaScript 中并沒有真正的類,但JavaScript 中有構(gòu)造函數(shù)和new 運(yùn)算符。構(gòu)造函數(shù)用來給實(shí)例對(duì)象初始化屬性和值。任何JavaScript 函數(shù)都可以用做構(gòu)造函數(shù),構(gòu)造函數(shù)必須使用new 運(yùn)算符作為前綴來創(chuàng)建新的實(shí)例。
new 運(yùn)算符改變了函數(shù)的執(zhí)行上下文,同時(shí)改變了return 語句的行為。實(shí)際上,使用new和構(gòu)造函數(shù)和傳統(tǒng)的實(shí)現(xiàn)了類的語言中的使用方法是很類似的:
var Person = function(name) {
this.name = name;
};
// 實(shí)例化一個(gè)Person
var alice = new Person('alice');
// 檢查這個(gè)實(shí)例
assert( alice instanceof Person );
要不然就返回任意非原始類型的值。比如,我們可以返回一個(gè)用以新建一個(gè)新類的函數(shù),第一步要做的是創(chuàng)建自己的類模擬庫(kù):
var Class=function(){
var klass=function(){
this.init.apply(this,arguments);
}
klass.prototype.inti=function(){};
return klass;
}
var Person=new Class; //具體參見new的用法 此時(shí)的Person等于的是一個(gè)function
Peson.prototype.init=function(){
//基于Person的實(shí)例做初始化
}
5、在JavaScript中,在構(gòu)造函數(shù)中給類添加函數(shù)和給對(duì)象添加屬性是一模一樣
Person.find=functon(){};
var person=Perons.find();
要想給構(gòu)造函數(shù)添加實(shí)例函數(shù),則需要用到構(gòu)造函數(shù)的prototype
Person.prototype.breath=function(){};
var person=new Person();
person.breath();
一般常用的模式是給類的prototype起一個(gè)別名,fn
Person.fn=Person.proptype;
Person.fn.run=function(){ };
6、給類庫(kù)添加方法
直接給類設(shè)置屬性和設(shè)置其靜態(tài)成員是等價(jià)的:
var Person = new Class;
// 直接給類添加靜態(tài)方法
Person.find = function(id){ /* ... / };
// 這樣我們可以直接調(diào)用它們
var person = Person.find(1);
給類的原型設(shè)置的屬性在類的實(shí)例中也是可用的:
var Person = new Class;
// 在原型中定義函數(shù)
Person.prototype.save = function(){ / ... */ };
// 這樣就可以在實(shí)例中調(diào)用它們
var person = new Person;
person.save();
我們采用另外一種不同的方法來給類添加屬性,這里用到了兩個(gè)函數(shù)extend和include
var class=function(){
var klass=function(){
this.init.apply(this,arguments);
};
klass.prototype.init=function(){};
//定義prototype別名
klass.fn=klass.prototype;
//定義類的別名
klass.fn.parent=klass;
//給類添加屬性
klass.extend=function(obj){
var extended=obj.extended;
for(var i in obj){
klass[i]=obj[i];
}
if(extended) extended(klass);
};
//給類添加屬性
klass.inclue=function(obj){
var inclued=obj.inclued;
for(var i in obj){
klass.fn[i]=obj[i];
}
if(inclued) inclued(klass);
};
}
var Person=new Class;
Person.extend({
find:function(id){}
exists:functions(id){}
});
var person=Person.find(1);
include() 函數(shù)的工作原理也是一樣的,只不過不是將屬性復(fù)制至類中,而是復(fù)制至類的原型中。換句話說,這里的屬性是類實(shí)例的屬性,而不是類的靜態(tài)屬性。
var Person=new Class;
Person.include({
save:function(){},
save:functions(id){}
})
var person=new Person;
person.save();
7、基于原型的類繼承
如果你給Array.prototype 添加了屬性,那么所有的JavaScript 數(shù)組都具有了這些屬性。代碼如下:
var Animal=function(){};
Animation.prototype.breath=function(){
console.log("breath");
}
var Dog=function(){};
//通過原型基礎(chǔ)
Dog.prototype=new Animal;
Dog.prototype.wag=function(){
console.log("wag')
}
實(shí)例
var dog=new Dog();
dog.breath();
dog.wag();
給“類”庫(kù)添加繼承
現(xiàn)在來給我們自定義的“類”庫(kù)添加繼承,我們通過傳入一個(gè)可選的父類來創(chuàng)建新類:
var Class=function(){
var klass=funtion(){
this.init.apply(this,arguments);
};
//改變class原型
if(parent){
var subclass=function(){};
subclass.prototype=parent.prototype;
klass.prototype=new subclass();
};
kclass.prototype.inti=function(){};
//定義別名
klass.fn=klass.prototype;
kclass.fn.parent=klass;
kclass._super=kclass.__prototype__;
/* include/extend 相關(guān)的代碼…… */
return klass;
}
var Class=function(){
var klass=funtion(){
this.init.apply(this,arguments);
};
//改變class原型
if(parent){
var subclass=function(){};
subclass.prototype=parent.prototype;
klass.prototype=new subclass();
};
kclass.prototype.inti=function(){};
//定義別名
klass.fn=klass.prototype;
kclass.fn.parent=kclass;
kclass._super=kclass.__prototype__;
/* include/extend 相關(guān)的代碼…… */
klass.extend=function(obj){
var extended=obj.extended;
for(var i in obj){
klass[i]=obj[i];
}
if(extended) extended(klass);
};
//給類添加屬性
klass.inclue=function(obj){
var inclued=obj.inclued;
for(var i in obj){
klass.fn[i]=obj[i];
}
if(inclued) inclued(klass);
};
return klass;
}
8、函數(shù)調(diào)用
在javascript中,函數(shù)和其他東西都是對(duì)象,然而函數(shù)可調(diào)用。
jQuery 在其API 的實(shí)現(xiàn)中就利用了apply() 和call() 來更改上下文,比如在事件處理程序中或者使用each() 來做迭代時(shí)。起初這很讓人費(fèi)解,一旦你理解了就會(huì)發(fā)現(xiàn)它非常有用:
$('.clicky’).click(function(){
// ‘this’指向當(dāng)前節(jié)點(diǎn)
$(this).hide();
});
$('p’).each(function(){
// ‘this’指向本次迭代
$(this).remove();
});
為了訪問原始上下文,可以將this的值存入一個(gè)局部變量中,這是一個(gè)常見的模式;
var clicky=function(){
wasClicked:function(){};
addListeners:function(){
var self=this;
$('.cicky').click(
fuction(){
sele.wasCliked();
};
)
}
}
clicky.addListeners();
然而,我們可以使用apply 來將這段代碼變得更干凈一些,通過將回調(diào)包裝在另外一個(gè)匿名函數(shù)中,來保持原始的上下文:
var proxy=function(func,thisObject){
return (
function (){
return func.apply(thisObject,arguments);//改變func的執(zhí)行對(duì)象
}
)
}
var clicky={
wasClicked:function{};
addListener:function(){
var self=this;
$('.clicky').click(proxy(this.wasClicked,this));
}
}
var App {
log: function(){
if (typeof console == "undefined") return;
// 將參數(shù)轉(zhuǎn)換為合適的數(shù)組
var args = jQuery.makeArray(arguments);
// 插入一個(gè)新的參數(shù)
args.unshift("(App)");
// 委托給console
console.log.apply(console, args);
}
};
9、控制類庫(kù)的作用域
我們?cè)陬惡蛯?shí)例中都添加proxy 函數(shù),這樣就可以在事件處理程序之外處理函數(shù)的時(shí)候和下面這段代碼所示的場(chǎng)景中保持類的作用域:
var Class=function(parent){
var klass=function(){
this.init.apply(this,argument)
};
klass.prototype.init=function(){};
klass.fn=klass.prototype;
klass.fn.parent=ckass;
//添加一個(gè)poxy函數(shù)
klass.proxy=function(func){
var self=this;
return (function(){
return func.apply(self,arguments)
})
}
//在實(shí)例中冶添加
klass.prototype=klass.proxy;
return klass;
}
現(xiàn)在使用proxy函數(shù)來包裝函數(shù),以包裝在作用域中正確調(diào)用
var Button=new Class();
Button.include({
init:function(){
this.element=jQuery(element);
//代理click函數(shù)
this.element.click(this.proxy(this.click));
//this.element.click(this.click.apply(this.argument))
},
click:function(){}
});
9、添加私有函數(shù)
通過創(chuàng)建匿名函數(shù)的方式來創(chuàng)建私有作用域;
var Person = function(){};
(function(){
var findById = function(){ /* ... */ };
Person.find = function(id){
if (typeof id == "integer")
return findById(id);
};
})();
我們將類的屬性都包裝進(jìn)一個(gè)匿名函數(shù)中,然后創(chuàng)建了局部變量(findById),這些局部變量只能在當(dāng)前作用域中被訪問到。Person 變量是在全局作用域中定義的,因此可以在任何地方都能訪問到。
定義變量的時(shí)候不要丟掉var 運(yùn)算符,因?yàn)槿绻麃G掉var 就會(huì)創(chuàng)建全局變量。如果你需要定義全局變量,在全局作用域中定義它或者定義為window 的屬性:
(function(exports){
var foo = "bar";
// 將變量暴露出去
exports.foo = foo;
})(window);
assertEqual(foo, "bar");