《Javascript高級程序設計 (第三版)》第六章 面向對象的程序設計

理解對象

屬性類型

  • 數據屬性
    數據屬性包含一個數據值的位置。在這個位置能夠讀取和寫入值。數據屬性有 4 個描述其行爲的特性。
[[Configurable]] :表示可否經過 delete 刪除屬性從而從新定義屬性,可否修改屬性的特
性,或者可否把屬性修改成訪問器屬性。像前面例子中那樣直接在對象上定義的屬性,它們的
這個特性默認值爲 true 。
[[Enumerable]] :表示可否經過 for-in 循環返回屬性。像前面例子中那樣直接在對象上定
義的屬性,它們的這個特性默認值爲 true 。
[[Writable]] :表示可否修改屬性的值。像前面例子中那樣直接在對象上定義的屬性,它們的
這個特性默認值爲 true 。
[[Value]] :包含這個屬性的數據值。讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,
把新值保存在這個位置。這個特性的默認值爲 undefined 。
var person = {};
Object.defineProperty(person, "name", {
    writable: false,
    value: "Nicholas"
});
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"


一旦把屬性定義爲不可配置的,就不能再把它變回可配置了。
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
delete person.name;
alert(person.name); //"Nicholas"
  • 訪問器屬性
    訪問器屬性不包含數據值;它們包含一對兒 getter 和 setter 函數(不過,這兩個函數都不是必需的)。在讀取訪問器屬性時,會調用 getter 函數,這個函數負責返回有效的值;在寫入訪問器屬性時,會調用setter 函數並傳入新值,這個函數負責決定如何處理數據。(只指定其中get或者set意味着屬性是不能寫,嘗試寫入屬性會被忽略。)
[[Configurable]] :表示可否經過 delete 刪除屬性從而從新定義屬性,可否修改屬性的特性,或者可否把屬性修改成數據屬性。對於直接在對象上定義的屬性,這個特性的默認值爲true 。
[[Enumerable]] :表示可否經過 for-in 循環返回屬性。對於直接在對象上定義的屬性,這個特性的默認值爲 true 。
[[Get]] :在讀取屬性時調用的函數。默認值爲 undefined 。
[[Set]] :在寫入屬性時調用的函數。默認值爲 undefined 。

var book = {
    _year: 2004,
    edition: 1
};
Object.defineProperty(book, "year", {
    get: function(){
        return this._year;
    },
    set: function(newValue){
        if (newValue > 2004) {
            this._year = newValue;
            this.edition += newValue - 2004;
        }
    }
});
book.year = 2005;
alert(book.edition); //2

定義多個屬性

  • Object.defineProperties()
var book = {};
Object.defineProperties(book, {
    _year: {
        value: 2004
    },
    edition: {
        value: 1
    },
    year: {
        get: function(){
            return this._year;
        },
        set: function(newValue){
            if (newValue > 2004) {
                this._year = newValue;
                this.edition += newValue - 2004;
            }
        }
    }
});

讀取屬性的特性

  • Object.getOwnPropertyDescriptor()
    能夠取得給定屬性的描述符。
var book = {};
Object.defineProperties(book, {
    _year: {
        value: 2004
    },
    edition: {
        value: 1
    },
    year: {
        get: function(){
            return this._year;
        },
        set: function(newValue){
            if (newValue > 2004) {
                this._year = newValue;
                this.edition += newValue - 2004;
            }
        }
    }
});
var descriptor =Object.getOwnPropertyDescriptor(book,"_year");
alert(descriptor.value); //2004
alert(descriptor.configurable); //false
alert(typeof descriptor.get); //"undefined"
var descriptor =Object.getOwnPropertyDescriptor(book, "year");
alert(descriptor.value); //undefined
alert(descriptor.enumerable); //false
alert(typeof descriptor.get); //"function"

建立對象

工廠模式

用函數來封裝以特定接口建立對象的細節構造函數模式
缺陷:沒有解決對象識別的問題安全


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");

構造函數模式

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");"
  • 將構造函數看成函數
// 看成構造函數使用
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName(); //"Nicholas"
// 做爲普通函數調用
Person("Greg", 27, "Doctor"); // 添加到 window
window.sayName(); //"Greg"
// 在另外一個對象的做用域中調用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); //"Kristen"
  • 構造函數的問題
    每一個方法都要在每一個實例上從新建立一遍。
function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = new Function("alert(this.name)"); //  與聲明函數在邏輯上是等價的
}
alert(person1.sayName == person2.sayName); //false

原型模式

每一個函數都有一個 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
  • 對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性。
  • 把原型修改成另一個對象就等於切斷了構造函數與最初原型之間的聯繫。
function Person(){}
var friend = new Person();
Person.prototype = {
    constructor: Person,
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};
friend.sayName(); //error

組合使用構造函數模式

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);
    }
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

動態原型模式

經過檢查某個應該存在的方法是否有效,來決定是否須要初始化原型。this

function Person(name, age, job){
    //屬性
    this.name = name;
    this.age = age;
    this.job = job;
// 方法
    if (typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
        alert(this.name);
        };
    }
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

寄生構造函數模式

建立一個函數,該函數的做用僅僅是封裝建立對象的代碼,而後再返回新建立的對象prototype

function Person(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 friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"

穩妥構造函數模式

沒有公共屬性,並且其方法也不引用 this 的對象。穩妥對象最適合在一些安全的環境中(這些環境中會禁止使用 this 和 new ),或者在防止數據被其餘應用程序(如 Mashup程序)改動時使用。指針

function Person(name, age, job){
//建立要返回的對象
    var o = new Object();
//能夠在這裏定義私有變量和函數
//添加方法
    o.sayName = function(){
        alert(name);
    };
//返回對象
    return o;
}

繼承

依靠原型鏈來實現code

原型鏈

function SuperType(){
    this.colors = ["red", "blue", "green"];
}
function SubType(){}
//繼承了 SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green,black"

借用構造函數

function SuperType(){
    this.colors = ["red", "blue", "green"];
}
function SubType(){
// 繼承了 SuperType
    SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"

組合繼承

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name, age){
    //繼承屬性
    SuperType.call(this, name);
    this.age = age;
}
//繼承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

原型式繼承

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

寄生組合式繼承

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name, age){
    //繼承屬性
    SuperType.call(this, name);
    this.age = age;
}
//繼承方法
SubType.prototype =SuperType.prototype;
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
相關文章
相關標籤/搜索