成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

紅寶書筆記-第6章-面向?qū)ο蟮某绦蛟O(shè)計(jì)

hizengzeng / 3598人閱讀

摘要:構(gòu)造函數(shù)本身也是函數(shù),只不過可以用來(lái)創(chuàng)建對(duì)象而已。在創(chuàng)建子類型的實(shí)例時(shí),沒有辦法在不影響所有對(duì)象實(shí)例的情況下,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。借用構(gòu)造函數(shù)又叫偽造對(duì)象或經(jīng)典繼承。

本章內(nèi)容

理解對(duì)象屬性

理解并創(chuàng)建對(duì)象

理解繼承

ECMA-262 把對(duì)象定義為:“無(wú)序?qū)傩缘募?,其屬性可以包含基本值、?duì)象或者函數(shù)?!眹?yán)格來(lái)講,這就相當(dāng)于說(shuō)對(duì)象是一組沒有特定順序的值。

每個(gè)對(duì)象都是基于一個(gè)引用類型創(chuàng)建的,既可以是原生類型,也可以是開發(fā)人員定義的類型。

6.1 理解對(duì)象

創(chuàng)建對(duì)象最簡(jiǎn)單的方式就是創(chuàng)建一個(gè) Object 的實(shí)例,然后為它添加屬性和方法。

var person = {
    name: "Jack",
    age: 29,
    sayName: function() {
        alert(this.name);
    }
}

這些屬性在創(chuàng)建時(shí)都帶有一些特征值(characteristic),JS 通過這些特征值來(lái)定義它們的行為。

6.1.1 屬性類型

ECMAScript 中有兩種屬性:數(shù)據(jù)屬性和訪問器屬性。

1. 數(shù)據(jù)屬性

數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位置。在這個(gè)位置可以讀取和寫入值。數(shù)據(jù)屬性有4個(gè)描述其行為的特性。

[[Configurable]]

[[Enumerable]]

[[Writable]]

[[Value]]

對(duì)于直接在對(duì)象上定義的屬性,它們的 [[Configurable]]、[[Enumerable]]、[[Writable]] 特性都被設(shè)置為 true,而 [[Value]] 特性被設(shè)置為指定的值。

要修改屬性默認(rèn)的特性,必須使用 ECMAScript 5 的 Object.defineProperty() 方法。接收3個(gè)參數(shù):屬性所在的對(duì)象、屬性名字、一個(gè)描述符對(duì)象。其中描述符(descriptor)對(duì)象的屬性必須是:configurable/enumerable/writable/value。設(shè)置其中的一個(gè)或多個(gè)值,可以修改對(duì)應(yīng)的特性值。

var person = {}
Object.defineProperty(person, "name", {
    writable: false,
    configurable: false,
    value: "Nick"
})
alert(person.name); // Nick
person.name = Jack;
alert(person.name); // Nick
delete person.name;
alert(person.name); // Nick

注意:一旦把屬性定義為不可配置的,就不能再把它變回可配置了。也就是說(shuō),可以多次調(diào)用 Object.defineProperty()方法修改同一個(gè)屬性,但在把 configurable 設(shè)置為 false 后,就不能了。

在調(diào)用 Object.defineProperty() 時(shí),如果不指定,則 configurable/writable/enumerable 都為 false。

2. 訪問器屬性

訪問器屬性不包含數(shù)據(jù)值;它們包含一對(duì) getter/setter 函數(shù)(不過,這兩個(gè)函數(shù)都不是必須的)。在讀取訪問器屬性時(shí),會(huì)調(diào)用 getter 函數(shù),這個(gè)函數(shù)負(fù)責(zé)返回有效的值;在寫入訪問器屬性時(shí),會(huì)調(diào)用 setter 函數(shù)并傳入新值,這個(gè)函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù)。特性如下:

[[Configurable]]

[[[Enumerable]]

[[Get]]:在讀取屬性時(shí)調(diào)用的函數(shù),默認(rèn)值 undefined。

[[Set]]:在寫入屬性時(shí)調(diào)用的函數(shù),默認(rèn)值 undefined。

訪問器屬性不能直接定義,必須使用 Object.defineProperty() 。

var book = {
    _year: 2004,
    edition: 1
};

Object.defineProperty(book, "year", {// IE9+
    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

下劃線是一種常用的記號(hào),用于表示只能通過對(duì)象方法來(lái)訪問的屬性。
以上是使用訪問器屬性的常見方式,即設(shè)置一個(gè)屬性的值會(huì)導(dǎo)致其他屬性的變化。
不一定要同時(shí)指定 getter 和 setter。只指定 getter 表示屬性是不能寫,反之則表示屬性不能讀。

6.1.2 定義多個(gè)屬性

Object.defineProperties() 可以通過描述符一次性定義多個(gè)屬性。接收2個(gè)參數(shù):1、第一個(gè)對(duì)象是要添加和修改其屬性的對(duì)象;2、第二個(gè)對(duì)象的屬性與第一個(gè)對(duì)象中要添加或修改的屬性一一對(duì)應(yīng)。

var book = {};
Object.defineProperties(book, { // IE9+
 _year: {
     writable: true,
     value: 2004
 },
 edition: {
     writable: true,
     value: 1
 },
 year: {
     get: function() {
         return this._year;
     },
     set: function(newValue) {
         this._year = newValue;
         this.edition++;
     }
 }
})
6.1.3 讀取屬性的特性

使用 ECMAScript 5 中的 Object.getOwnPropertyDescriptor() IE9+ 方法,可以取得給定屬性的描述符。這個(gè)方法接收兩個(gè)參數(shù):1、屬性所在的對(duì)象;2、要讀取器描述符的屬性名稱。返回值是一個(gè)對(duì)象,如果是數(shù)據(jù)屬性,這個(gè)對(duì)象的屬性有 configurable/enumerable/writable/value,如果是訪問器屬性,則這個(gè)對(duì)象的屬性有 configurable/enumerable/get/set

// 使用前面的例子
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"
6.2 創(chuàng)建對(duì)象

問題:使用同一個(gè)接口創(chuàng)建很多對(duì)象,會(huì)產(chǎn)生大量的重復(fù)代碼。

6.2.1 工廠模式

工廠模式抽象了創(chuàng)建具體對(duì)象的過程。用函數(shù)來(lái)封裝以特定接口創(chuàng)建對(duì)象的細(xì)節(jié)。

function createPerson(name, age) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function() {
        alert(this.name);
    };
    return o;
}

var person1 = createPerson("Jack", 29);
var person2 = createPerson("Nick", 22);

工廠模式雖然解決了創(chuàng)建多個(gè)相似對(duì)象的問題,但是沒有解決對(duì)象識(shí)別的問題(即怎樣知道一個(gè)對(duì)象的類型)。

6.2.2 構(gòu)造函數(shù)模式

可以創(chuàng)建自定義的構(gòu)造函數(shù),從而定義自定義對(duì)象類型的屬性和方法。

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.sayName = function() {
        alert(this.name);
    }
}
var person1 = new Person("Jack", 23);
var person2 = new Person("Nick", 22);

構(gòu)造函數(shù)模式有以下幾個(gè)特點(diǎn):

沒有顯示地創(chuàng)建對(duì)象;

直接將屬性和方法賦給了this對(duì)象;

沒有return語(yǔ)句。

函數(shù)名開頭必須大寫。

構(gòu)造函數(shù)本身也是函數(shù),只不過可以用來(lái)創(chuàng)建對(duì)象而已。

要?jiǎng)?chuàng)建 Person 的新實(shí)例,必須使用 new 操作符。以這種方式調(diào)用構(gòu)造函數(shù)實(shí)際上會(huì)經(jīng)歷以下4個(gè)過程:

創(chuàng)建一個(gè)新對(duì)象;

將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此 this 就指向了這個(gè)新對(duì)象);

執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性);

返回新對(duì)象。

使用 instanceof 檢測(cè)對(duì)象類型:

alert(person1 instanceof Object); // true
alert(person1 instanceof Person); // true
alert(person2 instanceof Object); // true
alert(person2 instanceof Person); // true

創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來(lái)可以將它的實(shí)例標(biāo)識(shí)為一種特定的類型;而這正是構(gòu)造函數(shù)模式勝過工廠模式的地方。

1. 將構(gòu)造函數(shù)當(dāng)做函數(shù)
// 當(dāng)做構(gòu)造函數(shù)來(lái)使用
var person = new Person("Nick", 29);
person.sayName(); // "Nick"

// 當(dāng)做普通函數(shù)調(diào)用
Person("Nick", 29); // 添加到 window 對(duì)象
window.sayName(); // "Nick"

// 在另一個(gè)對(duì)象作用域中調(diào)用
var o = new Object();
Person.call(o, "Nick", 29);
o.sayName(); // "Nick"
2. 構(gòu)造函數(shù)的問題

每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍。在前面的例子中,person1 和 person2 的 sayName() 方法并不是同一個(gè) Function 的實(shí)例。因?yàn)楹瘮?shù)是對(duì)象,所以每定義一個(gè)函數(shù),也就實(shí)例化了一個(gè)對(duì)象。(new Function())。

解決的辦法,可以把函數(shù)定義移到構(gòu)造函數(shù)外部。

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.sayName = sayName;
}

function sayName() {
    alert(this.name);
}

var person1 = new Person("Jack", 23);
var person2 = new Person("Nick", 22);

但新問題是:在全局作用域定義的函數(shù)實(shí)際上只能被某個(gè)對(duì)象調(diào)用,這讓全局作用域名不副實(shí)。而且,如果對(duì)象需要定義很多方法,那么就要定義多個(gè)全局函數(shù),于是這個(gè)自定義的引用類型就沒有絲毫封裝性可言。

6.2.3 原型模式

每個(gè)函數(shù)都有一個(gè) prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,這個(gè)對(duì)象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。

也可以說(shuō) prototype 就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的對(duì)象實(shí)例的原型對(duì)象。使用原型對(duì)象的好處是可以讓所有“對(duì)象實(shí)例”共享“原型對(duì)象”所包含的屬性和方法。

6. 原型對(duì)象的問題

它省略了為構(gòu)造函數(shù)傳遞初始化參數(shù)這一環(huán)節(jié),結(jié)果所有實(shí)例在默認(rèn)情況下都將取得相同的屬性值。

原型模式的最大問題是它的共享的本性所導(dǎo)致的。這個(gè)問題在包含引用類型值的屬性上顯而易見。

function Person() {}

Person.prototype = {
    constructor: Person,
    friends: ["Jack"]
};

var person1 = new Person();
var person2 = new Person();

person1.friends.push("Nick");

alert(person1.friends); // "Jack, Nick"
alert(person2.friends); // "Jack, Nick"
alert(person1.friends === person2.friends); // true

實(shí)例一般都是要有自己的全部屬性的,然而由于 person1.friends 和 person2.friends 都指向同一個(gè)數(shù)組,導(dǎo)致修改其中一個(gè),就會(huì)在另一個(gè)上同步共享。

6.2.4 組合使用構(gòu)造函數(shù)模式和原型模式

構(gòu)造函數(shù)模式用于定義實(shí)例屬性,原型模式用于定義方法和共享的屬性。

每個(gè)實(shí)例都會(huì)擁有自己的一份實(shí)例屬性的副本,但同時(shí)又共享著對(duì)“方法”的引用,最大限度地節(jié)約了內(nèi)存。

這種混成模式還支持向構(gòu)造函數(shù)傳遞參數(shù)。

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.friends = ["Jack"];
}

Person.prototype = {
    constructor: Person,
    sayName: function() {
        alert(this.name);
    }
}

var person1 = new Person("Nick", 22);
var person2 = new Person("Mike", 21);

person1.friends.push("Jane");

alert(person1.friends); // "Jack, Jane"
alert(person2.friends); // "Jack"
alert(person1.friends === person2.friends); // false
alert(person1.sayName === person2.sayName); // true

混成模式中,不同實(shí)例引用了不同的數(shù)組,因此原型對(duì)象的問題解決了。

6.2.5 動(dòng)態(tài)原型模式
function Person(name, age) {
    // 屬性
    this.name = name;
    this.age = age;
    
    // 方法
    if (typeof this.sayName != "function") {
        Person.prototype.sayName: function() {
            alert(this.name);
        }
    }
}

var person1 = new Person("Nick", 22);
person1.sayName();

if 語(yǔ)句檢查的可以是初始化之后應(yīng)該存在的任何屬性或方法——不必用一大堆 if 語(yǔ)句檢查每個(gè)屬性和每個(gè)方法,只要檢查其中一個(gè)即可。

對(duì)于采用這種模式創(chuàng)建的對(duì)象,可以使用 instanceof 操作符確定它的類型。

使用動(dòng)態(tài)原型模式時(shí),不能使用對(duì)象字面量重寫原型,如果重寫,則會(huì)切斷現(xiàn)有實(shí)例與新原型之間的聯(lián)系。

6.2.6 寄生構(gòu)造函數(shù)模式 6.2.7 穩(wěn)妥構(gòu)造函數(shù)模式 6.3 繼承

由于函數(shù)沒有簽名,在ECMAScript 中無(wú)法實(shí)現(xiàn)【接口繼承】。ECMAScript 只支持【實(shí)現(xiàn)繼承】,而且其實(shí)現(xiàn)繼承主要依靠【原型鏈】來(lái)實(shí)現(xiàn)。

6.3.1 原型鏈

基本思想

利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。

構(gòu)造函數(shù)、原型、實(shí)例之間的關(guān)系:

每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象;

原型對(duì)象都有一個(gè)指向構(gòu)造函數(shù)的指針;

實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針[[Prototype]]

實(shí)現(xiàn)原型鏈的基本模式:

function A() {
    this.aproperty = true;
}

A.prototype.getAValue = function() {
    return this.property;
};

function B() {
    this.bproperty = false;
}

// 繼承了 A,創(chuàng)建了 B 的實(shí)例,并將實(shí)例賦給 B.prototype
B.prototype = new A();

B.prototype.getBValue = function() {
    return this.bproperty;
}

var instance = new B();
alert(instance.getAValue); // true

實(shí)現(xiàn)的本質(zhì)是重寫原型對(duì)象,代之以一個(gè)新實(shí)例的類型。原來(lái)存在于 A 的實(shí)例中的所有屬性和方法,現(xiàn)在也存在于 B.prototype 中。

1. 默認(rèn)原型

所有應(yīng)用類型默認(rèn)都繼承了 Object,而這個(gè)繼承也是通過原型鏈實(shí)現(xiàn)的。所有函數(shù)的默認(rèn)原型都是 Object 的實(shí)例,因此默認(rèn)原型都會(huì)包含一個(gè)內(nèi)部指針,指向 Object.prototype。這也是所有自定義類型都會(huì)繼承 toString()valueOf() 等默認(rèn)方法的根本原因。

2. 確定原型和實(shí)例的關(guān)系

可以通過兩種方式來(lái)確定原型和實(shí)例之間的關(guān)系。
方法一:instanceof,只要用這個(gè)操作符來(lái)測(cè)試實(shí)例和原型鏈中出現(xiàn)過的構(gòu)造函數(shù),結(jié)果就會(huì)返回 true。

alert(instance instanceof Object); // true
alert(instance instanceof A); // true
alert(isntance instanceof B); // true

由于原型鏈的關(guān)系,instance 是 Object、A、B 中任何一個(gè)類型的實(shí)例。

方法二:isPropertyOf,只要是原型鏈中出現(xiàn)過的原型,都可以說(shuō)是該原型鏈所派生的實(shí)例的原型,因此該方法也會(huì)返回 true。

alert(Object.prototype.isPropertyOf(instance)); // true
3. 謹(jǐn)慎地定義方法

子類型有時(shí)候需要覆蓋超類型中的某個(gè)方法,或者需要添加超類型中不存在的某個(gè)方法。給原型添加方法的代碼一定要放在替換原型的語(yǔ)句之后。

function A() {
    this.property = true;
}

A.prototype.getAValue = function() {
    return this.property;
};

function B() {
    this.bproperty = false;
}

// 繼承了 A
B.prototype = new A();

// 添加新方法
B.prototype.getBValue = function() {
    return this.bproperty;
}

// 重寫超類型方法
B.prototype.getAValue = function() {
    return false;
}

注意,通過 A 的實(shí)例調(diào)用 getAValue() 方法時(shí),仍然繼續(xù)調(diào)用原來(lái)的方法。

在通過原型鏈實(shí)現(xiàn)繼承時(shí),不能使用對(duì)象字面量創(chuàng)建原型方法。因?yàn)檫@樣做會(huì)重寫原型鏈。

function A() {
    this.property = true;
}

A.prototype.getAValue = function() {
    return this.property;
};

function B() {
    this.bproperty = false;
}

// 繼承了 A
B.prototype = new A();

// 添加新方法
B.prototype = {
  getBValue: function() {
      return this.bproperty;
  }
};

var instance = new B();
alert(instance.getAValue); // error!
4. 原型鏈的問題

最主要的問題來(lái)自包含引用類型值的原型。包含引用類型值的原型屬性會(huì)被所有實(shí)例共享;而這也正是為什么要在構(gòu)造函數(shù)中,而不是在原型對(duì)象中定義屬性的原因。在通過原型來(lái)實(shí)現(xiàn)繼承時(shí),原型實(shí)際上會(huì)變成另一個(gè)類型的實(shí)例。于是,原先的實(shí)例屬性也就順理成章地變成了現(xiàn)在的原型屬性了。

在創(chuàng)建子類型的實(shí)例時(shí),沒有辦法在不影響所有對(duì)象實(shí)例的情況下,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。因此,實(shí)踐中很少會(huì)多帶帶使用原型鏈。

6.3.2 借用構(gòu)造函數(shù)(constructor stealing)

又叫“偽造對(duì)象”或“經(jīng)典繼承”。
基本思想
在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對(duì)象,因此通過使用 apply() 和 call() 也可以在(將來(lái))新創(chuàng)建的對(duì)象上執(zhí)行構(gòu)造函數(shù)。

function A() {
    this.colors = ["red"];
}

function B() {
    // 繼承了 A
    A.call(this);
}

var instance1 = new B();
instance1.colors.push("blue");
alert(instance1.colors); // "red, blue"

var instance2 = new B();
alert(instance2.colors); // "red"
1. 傳遞參數(shù)

相對(duì)于原型鏈而言,借用構(gòu)造函數(shù)有一個(gè)很大的有時(shí),可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)。

function A(name) {
    this.name = name;
}

function B() {
    // 繼承了 A
    A.call(this, "Jack");
}

var instance1 = new B();
alert(instance1.name); // "Jack"

為了確保 A 構(gòu)造函數(shù)不會(huì)重寫子類型的屬性,可以在調(diào)用超類型構(gòu)造函數(shù)后,再添加應(yīng)該在子類型中定義的屬性。

2. 借用構(gòu)造函數(shù)的問題

方法都在構(gòu)造函數(shù)中定義,因此函數(shù)復(fù)用就無(wú)從談起;

在超類型的原型中定義的方法,對(duì)子類型而言也是不可見的,結(jié)果所有類型都只能使用構(gòu)造函數(shù)模式。因此借用構(gòu)造函數(shù)也很少多帶帶使用

6.3.3 組合繼承(combination inheritance)

又叫“偽經(jīng)典繼承”,組合了原型鏈繼承和借用構(gòu)造函數(shù)繼承。既通過在原型上定義方法實(shí)現(xiàn)了函數(shù)服用,又能保證每個(gè)實(shí)例都擁有自己的屬性。

function A(name) {
    this.name = name;
    this.colors = ["red"];
}

A.prototype.sayName = function() {
    alert(this.name);
};

function B(name, age) {
    // 繼承屬性
    A.call(this, name); // 第二次調(diào)用 A
    this.age = age;
}

// 繼承方法
B.prototype = new A(); // 第一次調(diào)用 A
B.prototype.constructor = B;
B.prototype.sayAge = function() {
    alert(this.age);
}

var instance1 = new B("Jack", 22);
instance1.colors.push("blue");
alert(instance1.colors); // "red, blue"
instance1.sayName(); // "Jack"
instance1.sayAge(); // 22

var instance2 = new B("Nick", 21);
alert(instance2.colors); // "red"
instance2.sayName(); // "Nick"
instance2.sayAge(); // 21

組合繼承避免了原型鏈和借用構(gòu)造函數(shù)的缺陷,融合了它們的優(yōu)點(diǎn),成為JS中最常用的繼承模式。而且,instanceof 和 isPropertyOf() 也能夠用于識(shí)別基于組合繼承創(chuàng)建的對(duì)象。

組合模式的問題

無(wú)論什么情況下,都會(huì)調(diào)用兩次超類型構(gòu)造函數(shù):一次是在創(chuàng)建子類型原型的時(shí)候,一次是在子類型構(gòu)造函數(shù)內(nèi)部。

6.3.4 原型式繼承
function object(o) {
    function F(){};
    F.prototype = o;
    return new F();
}

原型式繼承要求必須有一個(gè)對(duì)象作為另一個(gè)對(duì)象的基礎(chǔ)。

ECMAScript 5 中新增了 Object.create() 來(lái)規(guī)范原型式繼承。接收2個(gè)參數(shù):1、一個(gè)用做新對(duì)象原型的對(duì)象;2、(可選)一個(gè)為新對(duì)象定義額外屬性的對(duì)象。在傳入一個(gè)參數(shù)的情況下,Object.create() 和 object() 的行為相同。

var person = {};
var anotherPerson = Object.create(person);

如果只想讓一個(gè)對(duì)象與另一個(gè)對(duì)象保持類似的情況下,原型式繼承完全可以勝任。但是包含引用類型值的屬性始終都會(huì)共享相應(yīng)的值,這點(diǎn)與原型模式一樣。

6.3.5 寄生式繼承(parasitic)

它的思路與寄生構(gòu)造函數(shù)和工廠模式相似,即創(chuàng)建一個(gè)僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來(lái)增強(qiáng)對(duì)象,最后再像真地是它做了所有工作一樣返回對(duì)象。

function createAnother(original) {
    var clone = object(original); // 通過調(diào)用函數(shù)創(chuàng)建一個(gè)新對(duì)象
    clone.sayHi = function() { // 以某種方式增強(qiáng)對(duì)象
        alert("Hi");
    };
    return clone; // 返回對(duì)象
}

var person = {
    name: "Jack",
    friends: ["Nick", "Tony"]
};

var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // "Hi"

新對(duì)象不僅具有 person 的所有屬性和方法,還有自己的方法。

在主要考慮“對(duì)象”而不是“自定義類型”和“構(gòu)造函數(shù)”的情況下,寄生式繼承也是一種有用的模式。object() 并不是必需的;任何能夠返回新對(duì)象的函數(shù)都適用于該模式。

注意:使用寄生式繼承來(lái)為對(duì)象添加函數(shù),會(huì)由于不能做到函數(shù)復(fù)用而降低效率;這一點(diǎn)與構(gòu)造函數(shù)模式類似。

6.3.6 寄生組合式繼承

本質(zhì)上,就是使用“寄生式繼承”來(lái)繼承超類型的原型,再將結(jié)果指定給子類型的原型。

function inheritPrototype(sub, super) {
    var prototype = Object(super); // 創(chuàng)建對(duì)象
    prototype.constructor = sub; // 增強(qiáng)對(duì)象
    sub.prototype = prototype; // 指定對(duì)象
}

創(chuàng)建超類型原型的一個(gè)副本;

為創(chuàng)建的副本添加 constructor 屬性,從而彌補(bǔ)因【重寫原型】而失去的默認(rèn)的 constructor 屬性;

將新創(chuàng)建的對(duì)象(即副本)賦值給子類型的原型。

修改之前的例子:

function A(name) {
    this.name = name;
}

A.prototype.sayName = function() {
    alert(this.name);
};

function B(age) {
    A.call(this, "Jack");
    this.age = age;
}

inheritPrototype(B,A);

B.prototype.sayAge = function() {
    alert(this.age);
}

該模式的高效率體現(xiàn)在它只調(diào)用了一次 A 構(gòu)造函數(shù),并且因此避免了在 B 的 prototype 上面創(chuàng)建不必要的、多余的屬性。與此同時(shí),原型鏈還能保持不變;因此,還能夠正常使用 instanceof 和 isPrototypeOf() 方法。開發(fā)人員普遍認(rèn)為寄生組合式繼承是引用類型最理想的繼承范式。

6.4 小結(jié)

ECMAScript 支持面向?qū)ο螅∣O)變成,但不使用類或者接口。對(duì)象可以在代碼執(zhí)行過程中創(chuàng)建和增強(qiáng),因此具有動(dòng)態(tài)性而非嚴(yán)格定義的實(shí)體。在沒有類的情況下,可以采用下列模式創(chuàng)建對(duì)象:

工廠模式,使用簡(jiǎn)單的函數(shù)創(chuàng)建對(duì)象,為對(duì)象添加屬性和方法,然后返回對(duì)象。這個(gè)模式后來(lái)被構(gòu)造函數(shù)所取代。

構(gòu)造函數(shù)模式,可以創(chuàng)建自定義引用類型,可以像創(chuàng)建內(nèi)置對(duì)象實(shí)例一樣使用 new 操作符。不過,構(gòu)造函數(shù)模式的缺點(diǎn)是:它的每個(gè)成員都無(wú)法得到復(fù)用,包括函數(shù)。由于函數(shù)可以不局限于任何對(duì)象(即與對(duì)象具有松散耦合的特點(diǎn)),因此沒有理由不在多個(gè)對(duì)象間共享函數(shù)。

原型模式,使用構(gòu)造函數(shù)的 prototype 屬性來(lái)指定那些應(yīng)該共享的屬性和方法。組合使用構(gòu)造函數(shù)模式和原型模式時(shí),使用構(gòu)造函數(shù)定義實(shí)例屬性,使用原型模式定義共享的屬性和方法。

JS 主要通過原型鏈實(shí)現(xiàn)繼承。原型鏈的構(gòu)建是通過將一個(gè)類型的實(shí)例賦值給另外一個(gè)構(gòu)造函數(shù)的原型實(shí)現(xiàn)的。這樣,子類型就可以繼承超類型的屬性和方法,這一點(diǎn)與基于類的繼承很相似。

原型鏈的問題是:對(duì)象實(shí)例共享所有繼承的屬性和方法,因此不適宜多帶帶使用。解決這個(gè)問題的技術(shù)是借用構(gòu)造函數(shù),即在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。這樣就可以做到每個(gè)實(shí)例都具有自己的屬性,同時(shí)還能保證只使用構(gòu)造函數(shù)模式來(lái)定義類型。

使用最多的繼承模式是組合繼承,這種模式使用原型鏈繼承共享的屬性和方法,通過借用構(gòu)造函數(shù)繼承實(shí)例屬性。
此外,還存在下列可供選擇的繼承模式:

原型式繼承,可以在不必預(yù)先定義構(gòu)造函數(shù)的情況下實(shí)現(xiàn)繼承,其本質(zhì)是執(zhí)行對(duì)給定對(duì)象的淺復(fù)制。而復(fù)制得到的副本還可以得到進(jìn)一步改造。

寄生式繼承,與原型式繼承非常相似,也是基于某個(gè)對(duì)象或某些信息創(chuàng)建一個(gè)對(duì)象,然后增強(qiáng)該對(duì)象,最后返回對(duì)象。為了解決組合繼承模式由于多次調(diào)用超類型構(gòu)造函數(shù)而導(dǎo)致的低效率問題,可以將這個(gè)模式與組合繼承模式一起使用。

寄生組合式繼承,集寄生式繼承與組合繼承的優(yōu)點(diǎn)于一身,是實(shí)現(xiàn)基于類型繼承的最有效方式。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/106761.html

相關(guān)文章

  • 寶書筆記-3-基本概念

    showImg(https://segmentfault.com/img/bVbwbVl?w=1649&h=2769);

    _ivan 評(píng)論0 收藏0
  • 寶書筆記-5-引用類型

    showImg(https://segmentfault.com/img/bVbwihz?w=1558&h=6145);

    ZoomQuiet 評(píng)論0 收藏0
  • 寶書筆記-4-變量、作用域和內(nèi)存問題

    showImg(https://segmentfault.com/img/bVbwfHP?w=1919&h=2347);

    nemo 評(píng)論0 收藏0
  • JS學(xué)習(xí)筆記6)(面向對(duì)象程序設(shè)計(jì)之理解對(duì)象

    摘要:其中,描述符對(duì)象的屬性必須是和。吧設(shè)置為,表示不能從對(duì)象中刪除屬性。這個(gè)方法接收兩個(gè)對(duì)象參數(shù)要添加和修改其屬性值的對(duì)象,第二個(gè)是與第一個(gè)對(duì)象中要添加和修改的屬性值一一對(duì)應(yīng)。 理解對(duì)象 1、創(chuàng)建自定義對(duì)象的兩種方法: (1)創(chuàng)建一個(gè)Object實(shí)例,然后再為它添加屬性和方法。 var person = new Object(); person.name = Nicholas; ...

    FingerLiu 評(píng)論0 收藏0
  • 深入JavaScript(一)this & Prototype

    摘要:然而事實(shí)上并不是。函數(shù)本身也是一個(gè)對(duì)象,但是給這個(gè)對(duì)象添加屬性并不能影響。一圖勝千言作者給出的解決方案,沒有麻煩的,沒有虛偽的,沒有混淆視線的,原型鏈連接不再赤裸裸。所以是這樣的一個(gè)函數(shù)以為構(gòu)造函數(shù),為原型。 注意:本文章是個(gè)人《You Don’t Know JS》的讀書筆記。在看backbone源碼的時(shí)候看到這么一小段,看上去很小,其實(shí)忽略了也沒有太大理解的問題。但是不知道為什么,我...

    The question 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<