JavaScript面向對象程序設計

JavaScript中沒有類的概念,它不是嚴格意義上的面嚮對象語言,而是基於對象(Object-based)的編程語言。下面是讀《JavaScript高級程序設計(第三版)》的學習筆記,總結一些經常使用的建立對象和繼承的方法。html

1、建立對象

1. 對象字面量

建立對象最簡單的方式就是建立一個Object的實例。經過先建立一個對象,再爲它添加屬性和方法。此方法用對象字面量方式更爲直觀:
var animal = {
    name : "mimi",
    sayName : function(){
        console.log(this.name);
    }
    //...
    }

★ 這種方法在建立多個對象時會產生大量重複的代碼,在建立單個對象時的首選模式。編程

2. 工廠模式

這種方法用函數來封裝以特定接口建立對象的細節:app

function animal(name) {
    var o = new Object();//建立新對象
    o.name = "mimi";//給這個對象添加屬性
    //...
    return o;//返回這個對象
}

★ 這種方法沒有解決對象識別的方法,必定程度上解決建立多個類似對象的問題吧,不常使用。編程語言

3. 構造函數模式

這種方法建立自定義的構造函數,從而自定義對象類型的屬性和方法。函數

function Animal(name) {
    this.name = name;//將屬性和方法賦給this對象
    this.sayName = function(){
        console.log(this.name);
    }
    //...
}

它沒有顯式的建立對象也沒有返回語句,但在使用new操做符調用後經歷了四個步驟:
(1)建立一個對象
(2)將函數的做用域賦給新對象(this指向了新對象)
(3)執行函數中代碼
(4)返回新對象性能

全部對象都有一個constructor屬性,指向其構造函數。constructor能夠用來標識對象類型,可是,要檢測對象類型,instanceof操做符更可靠。學習

★ 這種方法的問題在於,每一個方法都會在每一個實例上從新建立一次。this

4. 原型模式

function Animal() {
}
Animal.prototype.name = "mimi";//將方法屬性添加到Animal的prototype屬性中
Animal.prototype.sayName = function(){
    console.log(this.name);
};
//...

不管什麼時候,建立一個函數,都會自動建立一個prototype屬性,指向其原型對象,正如前面所說,每一個對象都有一個constructor屬性,指向其構造函數。因此Animal.prototype.constructor指向Animal。判斷原型對象與實例間關係可用isPrototypeOf()方法:spa

Animal.prototype.isPrototypeOf(animal1);

判斷屬性存在於實例中,仍是存在與原型中:
屬性存在於實例中時:prototype

animal1.hasOwnProperty(name);//true

屬性能經過對象訪問:

name in animal1;//true
  • 在實例中添加了與原型中同名的屬性

咱們將在實例中建立該屬性,而屏蔽原型中的屬性。固然,咱們能夠經過delete操做符徹底刪除實例中的該屬性而讓咱們從新訪問到原型中的屬性(*  ̄︿ ̄)。

  • 用字面量來實現更簡單的原型語法

function Animal() {
}
Animal.prototype = {
    constructor : Animal,//必須必須!由於這樣至關於建立了一個新對象並賦值給Animal.prototype,
//此時這個新對象的constructor爲默認的構造函數Object啊盆友們( ゚Д゚)ノ
    name : "mimi",
    sayName : function(){
    console.log(this.name);
    }
}

另外,很重要的一點:調用構造函數是會爲實例添加一個指向最初原型的[[prototype]]指針,這個鏈接存在與實例和構造函數的原型對象之間,而不是實例與構造函數間。
咱們將上面將上面的代碼稍加修改:

function Animal(name) {
}
animal1 = new Animal()//建立實例1
console.log(animal1.smell);//undefined;此時原型中還未添加smell屬性,理所固然。
Animal.prototype.smell = "good";//添加原型屬性
console.log(animal1.smell);//能夠訪問smell屬性。

然而,當用對象字面量來添加原型屬性時:

function Animal(name) {
}
animal1 = new Animal()//建立實例1
console.log(animal1.smell);//undefined;此時原型中還未添加smell屬性,理所固然。
Animal.prototype = {//添加原型屬性
    smell : "good";
}
console.log(animal1.smell);//undefined;
//由於animal1在建立時[[prototype]]指針指向的是最初的原型,而字面量法添加原型屬性時將對象原型改變了,但[[prototype]]沒有跟着一塊兒變化,因此沒法訪問。

★ 這個方法的問題在於:1.它沒辦法傳遞初始化參數 2.對於引用類型值的屬性(A)來講,改變一個實例的A屬性會直接引發全部實例中的A屬性的變化,由於實例中的A屬性只是其原型中A屬性的一個引用。

5. 組合使用構造函數模式和原型模式

構造函數模式用於定義實例屬性,原型模式用於定義方法和共享屬性。

function Animal(name) {//添加實例屬性
    this.name = name;
    this.color = ["yellow", "black"];
    //...
}
Animal.prototype = {//添加方法和共享屬性
    constructor : Animal,
    sayName : function(){
    console.log(this.name);
    }
    //...
}

★ 這個方法就是使用最普遍、認同度最高的建立自定義類型的方法啦。

小總結:第一種和最後一種是比較經常使用的方法了,書中還有提到一些方法,是在前面這些方法都不適用時能夠選擇使用的,不過我還沒碰上啦,因此等之後碰上了再來補充。

2、繼承

JavaScript中不支持接口繼承(繼承方法簽名),都是支持實現繼承(繼承實際的方法),主要依賴原型鏈來實現。

1. 原型鏈

將父類的實例賦值給子類的原型,此時,父類的實例中包含一個指向父類原型的指針。子類的原型經過這個指針訪問到父類原型中的屬性。

function Animal(name) {//父類
    this.name = name;
}
Animal.prototype = {//將屬性添加到父類原型
    constructor : Animal,
    sayName : function(){
    console.log(this.name);
    }
}
function Cat() {//子類
}
Cat.prototype = new Animal();//將父類實例賦值給子類原型。
Cat.prototype.constructor = Cat;//prototype被換成另外一個對象,因此constructor屬性要重寫,不然會指向Animal.
var cat1 = new Cat();

其中的原型鏈:

clipboard.png
用instanceof操做符斷定可得出cat1是Cat、Animal、Object的實例。

★ 此方法存在問題前面已有說起,就是包含引用類型值的原型屬性會被全部實例共享,且不能傳參。

2. 借用構造函數

在子類型構造函數的內部調用超類型構造函數,經過apply()和call()方法來實現。

function Animal(name) {//父類
    this.name = name;
}
function Cat(name) {//子類
    Animal.call(this, name);//執行了一遍父類型構造函數代碼
}
var cat1 = new Cat();

★ 此方法問題在於:1.方法都在構造函數中定義,函數複用無從談起。2.在超類的原型中定義的方法對子類型也不可見。並且用instanceof也沒法斷定cat1與Animal的聯繫。

3. 組合繼承

使用原型鏈實現對原型屬性和方法的繼承,使用借用構造函數實現對實例屬性的繼承。

function Animal(name) {//父類添加實例屬性
    this.name = name;
}
Animal.prototype = {//父類原型添加方法和屬性
    constructor : Animal,
    sayName : function(){
    console.log(this.name);
    }
}
function Cat(name) {//子類
    Animal.call(this, name);//繼承屬性
}
Cat.prototype = new Animal();//繼承方法
Cat.prototype.constructor = Cat;//將constructor指回子類

★ 此方法是JS中最經常使用的繼承模式。並且用instanceof 和isPrototypeOf() 也能識別。

目前有實際用的就這些了,總結的比較基礎,但願也總結清楚了。

參考文獻:《JavaScript高級程序設計(第三版)》

相關文章
相關標籤/搜索