《JavaScript高級程序設計》讀書筆記--6-面向對象的程序設計

理解對象

var person = {
    name: "Nicholas",
    age: "29",
    job: "Software Engineer",
    sayName: function(){
        alert(this.name);
    }
};

創建了person對象,添加了三個屬性和一個方法。
屬性類型
數據屬性包含一個數據值的位置,這個位置可以讀取和寫入值。有4個描述其行爲的特性:

  • [[Configurable]]:表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性或者能否把屬性修改爲訪問屬性。默認爲true。
  • [[Enumerable]]:表示能否通過for-in循環返回屬性,默認爲true。
  • [[Writable]]:表示能否修改屬性的值,默認爲true。
  • [[Value]]:包含這個屬性的數據值,讀取屬性值的時候從這個位置度,寫入屬性值的時候把新值保存在這個位置,默認爲undefined。
var person = {
    name: "Nicholas"
};

這裏創建了一個名爲name的屬性,爲它指定的值是Nicholas,也就是說[[Value]]特性將被設置爲Nicholas,而對這個值的任何修改都將反映在這個位置 。
修改屬性默認特性,必須使用Object.defineProperty()方法。其接收三個參數,對象、屬性名稱和描述符對象。在調用該方法時,如果不指定,configurable、enumerable和writable特性的默認值都是false。
訪問屬性不包含數據值,包含一對getter和setter函數。
Object.defineProperties()方法可以通過描述符定義多個屬性。
Object.getOwnPropertyDescriptor()方法可以讀取屬性的描述符。

創建對象

工廠模式

function createPerson(name,age,job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert(this.name);
    };
    return o;
}
var person1 = createPerson("Nicholas",29,"Software Engineer");
var person2 = createPerson("Greg",27,"Doctor");

函數createPerson()能夠根據接收的參數來構建一個包含所有必要信息的Person對象,可以無數次的調用這個函數,而每次他都會返回一個包含三個屬性和一個方法的對象。
構造函數模式

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };
}
var person1 = new Person("Nicholas",29,"Software Engineer");
var person2 = new Person("Greg",27,"Doctor");

這個例子中Person()函數取代了createPerson()函數,不同之處:

  • 沒有顯式地創建對象
  • 直接將屬性和方法賦給了this對象
  • 沒有return語句

此外,構造函數應以大寫字母開頭,非構造函數應以小寫字母開頭。
要創建Person的新實例必須要使用new操作符,以這種方式調用構造函數實際上會經歷以下4個步驟:

  1. 創建一個新對象
  2. 將構造函數的作用域賦給新對象(因此this就指向了這個新對象)
  3. 執行構造函數中的代碼(爲這個新對象添加屬性)
  4. 返回新對象

將構造函數當作函數
與其他函數的唯一區別在於調用方式的不同,任何函數只要通過new操作符來調用那他就可以作爲構造函數,而任何函數,如果不通過new操作符來調用,那他跟普通函數也不會有什麼兩樣。

構造函數的問題
就是每個方法都要在每個實例上重新創建一遍。
用原型模式解決。

原型模式
創建的每一個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法,使用原型對象的好處可以讓所有對象實例共享它所包含的屬性和方法。也就是說,不必在構造函數中定義對象的實例信息,而是可以將這些信息直接添加到原型對象中。

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype,age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
}
var person1 = new Person();
person1.sayName();//"Nicholas"
var person2 = new Person();
person2.sayName();//"Nicholas"
alert(person1.sayName == person2.sayName);//true

person1和person2都是訪問的同一組屬性和方法。
若實例中重新創建了與原型中相同名字的屬性,則該屬性將會屏蔽實例原型中的屬性。但是不會修改實例原型中的同名屬性。
使用hasOwnProperty()方法可以檢測一個屬性是存在於實例中還是原型中。存在於實例中則返回true。

原型與in操作符
單獨使用時,in操作符會在通過對象能夠訪問給定屬性時返回true,無論該屬性存在於實例中還是原型中。
使用for-in循環時,返回的是所有能夠通過對象訪問的、可枚舉屬性,其中既包括存在於實例中id屬性也包括存在於原型中的屬性。
Object.keys()方法可以取得對象上所有可枚舉的實例屬性。
Object.getOwnPropertyNames()方法返回所有的實例屬性,無論他是否可枚舉。

更簡單的原型語法

function Person(){
}
Person.prototype = {
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName: function(){
        alert(this.name);
    }
};

原型的動態性
實例中的指針僅指向原型,而不是指向構造函數。
儘管可以隨時爲原型添加屬性和方法,並且修改能夠立即在所有對象實例中反映出來,但是如果重寫整個原型對象,將會切斷現有原型和任何之前已經存在的對象實例之間的聯繫,他們引用的仍然是最初的原型。

原生對象的原型
不支持添加原生對象的原型屬性和方法。

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

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["shelby","Court"];
}
Person.prototype = {
    constructor: Person,
    sayName: function(){
        alert(this.name);
    }
};

動態原型模式
可以通過檢查摸個應該存在的方法是否有效來決定是否需要初始化原型。

if(typeof this.sayName!= "function"){
    Person.prototype.sayName = function(){
        alert(this.name);
    };
}

寄生構造函數模式
基本思想是創建一個函數,該函數的作用僅僅是封裝創建對象的代碼,然後再返回新創建的對象;但是從表面上看,這個函數又很像是典型的構造函數。

穩妥構造函數
所謂穩妥對象,是沒有公共屬性,其方法也不用引用this的對象,適合在一些安全的環境中或者防止數據被其他應用程序改動時使用 使用。
創建對象的實例方法不引用this,不使用new操作符調用構造函數。

繼承

js只支持實現繼承,而且其實現繼承主要是依靠原型鏈來實現。
原型鏈
基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。
構造函數、原型和實例的關係:
每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
};
function SubType(){
    this.subproperty = false;
}
//繼承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
    return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue());//true

關係圖
SubType繼承了SuperType,而SuperType繼承了Object。

確定原型和實例的關係
instanceof操作符,只要用這個操作符來測試實例與原型鏈中出現過的構造函數,結果就會返回true。
isPrototypeOf()方法,同樣返回true。

謹慎的定義方法不管怎樣,給原型添加方法的代碼一定要放在替換原型的語句之後。
在通過原型鏈實現繼承時,不能使用對象字面量創建原型方法,這樣會重寫原型鏈。

借用構造函數
基本思想即在子類型構造函數的內部調用超類型構造函數。

組合繼承
將原型鏈和借用構造函數的技術組合到一起,從而發揮二者之長的一種繼承模式。思路是使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數實現對實例屬性的繼承。這樣即通過在原型上定義方法實現了函數複用,又能夠保證每個實例都有他自己的屬性。

原型式繼承
寄生式繼承
寄生組合式繼承

發佈了42 篇原創文章 · 獲贊 58 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章