【JavaScript】面向物件與原型
ECMAScript有兩種開發模式:
1、函式式(過程化)
2、面向物件(OOP)
但是ECMAScript沒有類的概念,因此與基於類的語言的物件也有所不同。
一、建立物件
var box=new Object();
box.name='lee'; //屬性
box.run=function(){ //方法
returnthis.name+'執行中。。。';
}
alert(box.run());
關於工廠模式方法
因為物件是引用型別值,所以,每次更新這個物件,物件的源值都會改變,要想多整出幾個物件,就要再寫一個新的物件,從而產生2個不同的物件引用值,但是這樣每次建立一個新的物件,要寫很多程式碼,所以用到工廠模式方法。
程式碼:
function createObject(name,age){
var obj=new Object();
obj.name=name;
obj.age=age;
obj.run=function(){
returnthis.name+this.age+'
};
return obj;
}
var box1=createObject('lee',100);
var box2=createObject('kee',200);
alert(box1.run());
alert(box2.run());
工廠模式解決了重複例項化的問題。
關於建構函式
因為工廠模式解決了重複例項化的問題,但是還有一個問題就是識別問題,因為根本無法搞清楚他們到底是那個物件的例項,所以就採用建構函式方法來建立特定物件。
程式碼:
function Box(name,age){
this.name=name;
this.age=age;
this.run=function(){
return this.name+this.age+'執行中';
}
}
var box1=newBox('lee',100); //new Box()即可
var box2=newBox('kee', 200);
alert(box1.run());
alert( box1 instantof Box); //很清晰的識別他從屬於Box.
採用構造方法就解決了上面兩個問題,第一,重複例項化,第二,物件識別的問題,但是這裡了並沒有new Object(),為什麼可以例項化Box()?
採用構造方法,和使用工廠模式的方法不同之處:
1、建構函式方法沒有顯示的建立物件(new Object());
2、直接將屬性和方法賦值給this物件。
3、沒有return語句。
建構函式方法有一些規範:
1、函式名和例項化構造名相同且大寫(PS:非強制,但這麼寫有助於區分建構函式和普通函式)。
2、通過建構函式建立物件,必須使用new運算子。
上面說了,在建構函式方法裡面沒有new Object(),沒有建立物件,那麼它在什麼地方建立了一個新的物件?
1、使用了建構函式,並且new 建構函式(),那麼後臺就執行了new Object();
2、將建構函式的作用域給新物件,(即new Object()創建出來的物件),而函式體內的this就代表newObject()出來的物件。
3、返回新物件(後臺直接返回)
物件冒充呼叫:
為了改變作用域,就用到了物件冒出呼叫。
var 0=new Object();
Box.call(o,'jack',200)
alert(0.run());
這樣物件0就可以呼叫box的方法了。
二、原型
我們建立的每一個函式都有一個prototype()屬性,這個屬性是一個物件,用途是包含可以由特定型別的所有例項共享的屬性和方法。
原型的建立:
方法一:
functionBox(){} //宣告一個建構函式
Box.prototype.name='lee';
Box.prototype.age=100;
Box.prototype.run=function(){
return.this.name+this.age+'執行中。。';
}
方法二:
function Box(){};
Box.prototype={
name:'lee',
age:100,
run:function(){
return this.name+this.age+'執行中。。';
}
};
進一步瞭解建構函式的宣告方式和原型模式的宣告方式,下面是圖示;
判斷一個物件是否指向了該建構函式的原型物件,可以使用isPrototypeOf()方法來測試。
alert(Box.prototype.isPrototypeOf(box));
原型模式的執行流程:
1、先查詢建構函式例項裡的屬性或方法,如果有,立即返回。
2、如果建構函式例項裡沒有,就去它的原型物件裡找,如果有,就返回。
物件例項僅可以訪問原型中的值,不可更改。
判斷屬性是在建構函式還是在原型裡?
alert(box.hasOwnProperty('name')); //在例項裡返回true.
原型物件不僅可以在自定義物件的情況下使用,而ECMAScipt內建的引用型別都可以使用這種方式,並且內建的引用型別本身也使用了原型。
因為原型模式的最大優點:共享,也是它的最大的缺點,所以,當我們要保持資料的特性,而不能共享時,我們就用到下面:
組合建構函式+原型模式:
function Box(name,age){ //不共享的使用建構函式
this.name=name;
this.age=age;
this.family=['father'];
};
Box.prototype={ //共享時使用原型模式
constructor:Box,
run:function(){
returnthis.name+this.age+this.family;
}
};
為了封裝性更好,我們把建構函式和原型封裝在一起,叫做:
動態原型模式:
functionBox(name,age){ //將所有的寫成封裝到函式體內
this.name=name;
this.age=age;
if(typeofthis.run!='function'){ //僅在第一次呼叫的初始化
Box.prototype.run=function(){
return this.name+this.age+'執行中。。。';
};
}
}
Varbox=new Box('lee',100);
Alert(box.run());
三、繼承
繼承是面向物件中的一個比較核心的概念。其他正統面嚮物件語言都會用兩種方式實現繼承,一個是介面實現,一個是繼承,而ECMAScript只支援繼承,不支援介面實現,而實現繼承的方式依靠原型鏈完成。
在JavaScript裡,被繼承的函式稱為超型別(父類,基類),繼承的函式稱為子型別(子類,派生類)。繼承也有之前的問題,比如字面量重寫原型會中斷關係,使用引用型別的原型,子類還無法給超型別傳遞引數。
借用建構函式:
目的:
為了解決引用共享和超型別無法傳參的問題。
組合繼承:
目的:
在上面的基礎上,做到複用。
原型鏈+借用建構函式
組合繼承是JavaScript最常用的繼承模式,但有些小問題,就是超型別在使用過程中會被呼叫2次,一次是建立子型別的時候,另一次是在子型別建構函式的內部。
寄生組合繼承:
解決了組合繼承的2次呼叫的問題:
最後再來一張圖鎮樓~~