參考文章:
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
ExtJS 比較難入門的原因,主要是因爲其中用到了Javascript 中面向對象方面的東西,Extjs中有大量的類,而在實際使用時,也需要繼承Extjs標準類。我們先了解下Javascript中類相關的知識。Javascript是一種基於對象(object-based)的語言,你遇到的所有東西幾乎都是對象。但是,它又不是一種真正的面向對象編程(OOP)語言,因爲它的語法中沒有class(類)。這就導致Javascript的Object模型很獨特,和其他語言都不一樣。
生成對象的原始模式
定義類最簡單的方式就是用大括號,我們把貓看成一個對象,它有"名字"和"顏色"兩個屬性。
1
2
3
4
5
|
var cat1 = { name : '大毛' , color : '黃色' }; console.dir(cat1) |
這就是最簡單的封裝了,把兩個屬性封裝在一個對象裏面。
構造函數模式
爲了解決從原型對象生成實例的問題,Javascript提供了一個構造函數(Constructor)模式。所謂"構造函數",其實就是一個普通函數,但是內部使用了this變量。對構造函數使用new運算符,就能生成實例,並且this變量會綁定在實例對象上。
1
2
3
4
5
6
|
function Cat(name,color){ this .name = name; this .color = color; } var cat1 = new Cat( '大毛' , '黃色' ); console.dir(cat1); |
Javascript 規定,每個對象都有一個constructor屬性,指向構造函數。
1
|
alert(cat1.constructor == Cat); // true |
Javascript還提供了一個 instanceof 運算符,驗證類與實例對象之間的關係。
1
|
alert(cat1 instanceof Cat); // true |
構造函數方法很好用,但是存在浪費內存的問題。對象實例之間不能共享數據。Javascript規定,每一個構造函數都有一個prototype屬性,指向另一個對象。這個對象的所有屬性和方法,都會被構造函數的實例繼承。可以把那些不變的屬性和方法,直接定義在prototype對象上。
1
2
3
4
5
6
7
8
9
10
11
|
function Cat(name,color){ this .name = name; this .color = color; } Cat.prototype.type = '貓科動物' ; Cat.prototype.eat = function () { alert( '喫.....' ) }; var cat1 = new Cat( '大毛' , '黃色' ); var cat2 = new Cat( '二毛' , '黃色' ); alert(cat1.eat == cat2.eat); |
所有實例的type屬性和eat()方法,其實都是同一個內存地址,指向prototype對象,因此就提高了運行效率.
每個實例對象都有一個hasOwnProperty()方法,用來判斷某一個屬性到底是本地屬性,還是繼承自prototype對象的屬性
1
2
|
console.info(cat1.hasOwnProperty( "name" )); console.info(cat1.hasOwnProperty( "type" )); |
in運算符可以用來判斷,某個實例是否含有某個屬性,不管是不是本地屬性。in運算符還可以用來遍歷某個對象的所有屬性。
1
2
3
4
|
console.info( "name" in cat1); for ( var prop in cat1){ console.info( 'cat1[' +prop+ ']=' +cat1[prop]); } |
構造函數的繼承
Javascript 中的繼承全靠一種奇特的原型鏈(prototype chain)來實現。前面已經介紹了,Javascript 規定每個構造函數都有一個prototype 屬性,這個prototype屬性指向的對象的所有屬性和方法,都可以被這個構造函數所有。
1
2
3
4
5
6
7
|
function Animal(){ this .species = "動物" ; } function Cat(name,color){ this .name = name; this .color = color; } |
想讓Cat 是Animal的子類,可以通過Cat 的 prototype 屬性實現繼承。
1
2
3
4
|
Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; var cat1 = new Cat( "大毛" , "黃色" ); alert(cat1.species); // 動物 |
第一行,把 Cat 的原型屬性 prototype 指向 Animal,這 Cat 就可以訪問到 Animal 中的屬性和方法。
第二行,其實是手工糾正。原因是:每個對象都有一個constructor屬性,指向它的構造函數。其實每個prototype 也有一個constructor屬性。同時實例的constructor屬性,默認調用prototype對象的constructor屬性。如果重新指定了prototype屬性,那麼實例的constructor屬性 和 prototype對象的constructor屬性就不一致了。所以就需要手工糾正。
這是很重要的一點,編程時務必要遵守。即如果替換了prototype對象,下一步必然是爲新的prototype對象加上constructor屬性,並將這個屬性指回原來的構造函數。
1
2
|
o.prototype = {}; o.prototype.constructor = o;
|