国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

高程(第六章) 面向?qū)ο蟮某绦蛟O(shè)計(jì)

ranwu / 1204人閱讀

摘要:對(duì)于采用這種模式的對(duì)象,還可以使用操作符確定它的類(lèi)型寄生構(gòu)造函數(shù)模式通常,在前述的幾種模式都不適用的情況下,可以使用寄生構(gòu)造函數(shù)模式。這個(gè)模式可以在特殊的情況下用來(lái)為對(duì)象創(chuàng)建構(gòu)造函數(shù)。

ECMA-262把對(duì)象定義為:“無(wú)序?qū)傩缘募希鋵傩钥梢园局怠?duì)象或者函數(shù)”。嚴(yán)格來(lái)講,這就相當(dāng)于說(shuō)對(duì)象是一組沒(méi)有特定順序的值。

1 理解對(duì)象

創(chuàng)建對(duì)象:

var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";

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

字面量形式:

var person = {
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    sayName : function(){
        alert(this.name);
    };
}
1.1 屬性類(lèi)型

ECMA-262第5版在定義只有內(nèi)部才用的特性時(shí),描述了屬性的各種特性。ECMA-262定義這些特性是為了實(shí)現(xiàn)JavaScript引擎用的,因此在JavaScript中不能直接訪問(wèn)它們。
ECMAScript中有兩種屬性:數(shù)據(jù)屬性訪問(wèn)器屬性

1.1.1 數(shù)據(jù)屬性

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

[[Configurable]]:表示能否通過(guò)delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問(wèn)器屬性。像前面例子中那樣直接在對(duì)象上定義的屬性,它們的這個(gè)特性的屬性值為true

[[Enumerable]]:表示能否通過(guò)for-in循環(huán)返回屬性。像前面例子中那樣直接在對(duì)象上定義的屬性,它們的這個(gè)特性的默認(rèn)值為true

[[Writable]]:包含這個(gè)屬性的數(shù)據(jù)值。讀取屬性值的時(shí)候,從這個(gè)位置讀;寫(xiě)入屬性值的時(shí)候,把新值保存在這個(gè)位置。這個(gè)特性的默認(rèn)值為undefined

要修改屬性默認(rèn)的特性,必須使用ECMAScript 5Object.defineProperty()方法:

var person = {};
Object.defineProperty(person, "name", {
    writable : false,
    value : "Nicholas"
});

console.log(person.name);    //"Nicholas"
person.name = "Greg";
console.log(person.name);    //"Nicholas"

在嚴(yán)格模式下,上面的賦值操作將會(huì)導(dǎo)致拋出錯(cuò)誤

var person = {};
Object.defineProperty(person, "name", {
    configurable : false,
    value : "Nicholas"
});

console.log(person.name);    //"Nicholas"
delete person.name;
console.log(person.name);    //"Nicholas"

一旦把屬性定義為不可配置的,就不能再把它變回可配置了:

var person = {};
Object.defineProperty(person, "name", {
    configurable : false,
    value : "Nicholas"
});

Object.defineProperty(person, "name", {
    configurable : true,    //拋出錯(cuò)誤
    value : "Nicholas"
});

也就是說(shuō),可以多次調(diào)用Object.defineProperty()方法修改同一個(gè)屬性,但在把configurable特性設(shè)置為false之后就會(huì)有限制了

注意!利用Object.defineProperty()方法創(chuàng)建一個(gè)新的屬性時(shí),如果不指定,configurableenumerablewritable特性的默認(rèn)值都是false

1.1.2 訪問(wèn)器屬性

訪問(wèn)器有以下4個(gè)屬性:

[[Configurable]]:表示能否通過(guò)delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問(wèn)器屬性。對(duì)于直接在對(duì)象上定義的屬性,這個(gè)特性的屬性值為true

[[Enumerable]]:表示能否通過(guò)for-in循環(huán)返回屬性。對(duì)于直接在對(duì)象上定義的屬性,這個(gè)特性的默認(rèn)值為true

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

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

訪問(wèn)器屬性不能直接定義,必須使用Object.defineProperty()來(lái)定義:

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

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

book.year = 2005;
console.log(book.edition);    //2

不一定非要同時(shí)指定getter和setter。只指定getter意味著屬性是不能寫(xiě),嘗試寫(xiě)入屬性會(huì)被忽略。

1.2 定義多個(gè)屬性

Object.defineProperties():利用這個(gè)方法可以通過(guò)描述符一次定義多個(gè)屬性

var book = {};

Object.defineProperties(book, {
    _year : {
        writable : true,
        value : 2004
    },

    edition : {
        writable : true,
        value : 1
    },

    year : {
        get : function(){
            return this._year;
        },

        set : function(){
            if(newValue > 2004){
                this._year = newValue;
                this.edition += newValue - 2004;
            }
        }
    }
});
1.3 讀取屬性的特性

Object.getOwnPropertyDescriptor():可以取得給定屬性的描述符

//接上段代碼
    var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
    console.log(descriptor.value);                //2004
    console.log(descriptor.configurable);    //false
    console.log(typeof descriptor.get);        //"undefined"

    var descriptor = Object.getOwnPropertyDescriptor(book, "year");
    console.log(descriptor.value);                //undefined
    console.log(descriptor.enumerable);        //false
    console.log(typeof descriptor.get);        //"function"

對(duì)于訪問(wèn)器屬性yearget是一個(gè)指向getter函數(shù)的指針

2 創(chuàng)建對(duì)象

Object構(gòu)造函數(shù)或?qū)ο笞置媪慷伎梢杂脕?lái)創(chuàng)建單個(gè)對(duì)象,但這些方式有個(gè)明顯缺點(diǎn):使用同一個(gè)接口創(chuàng)建很多對(duì)象,會(huì)產(chǎn)生大量的重復(fù)代碼。為解決這個(gè)問(wèn)題,人們開(kāi)始使用工廠模式的一種變體。

2.1 工廠模式

工廠模式是軟件工程領(lǐng)域一種廣為人知的設(shè)計(jì)模式,這種模式抽象了創(chuàng)建具體對(duì)象的過(guò)程。考慮到在ECMAScript中無(wú)法創(chuàng)建類(lèi),開(kāi)發(fā)人員就發(fā)明了一種函數(shù),用函數(shù)來(lái)封裝以特定接口創(chuàng)建對(duì)象的細(xì)節(jié):

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

var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

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

2.2 構(gòu)造函數(shù)模式
function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function () {
    console.log(this.name);
  };
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

構(gòu)造函數(shù)模式與工廠模式的不同之處:

沒(méi)有顯式地創(chuàng)建對(duì)象;

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

沒(méi)有return語(yǔ)句。

按照慣例,構(gòu)造函數(shù)始終都應(yīng)該以一個(gè)大寫(xiě)字母開(kāi)頭,而非構(gòu)造函數(shù)則應(yīng)該以一個(gè)小寫(xiě)字母開(kāi)頭。

要?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ì)象。

console.log(person1.constructor == truPersone);   //true
console.log(person2.constructor == Person);   //true

console.log(person1 instanceof Object);     //true
console.log(person1 instanceof Person);     //true
console.log(person2 instanceof Object);     //true
console.log(person2 instanceof Person);     //true

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

這種方式定義的構(gòu)造函數(shù)是定義在Global對(duì)象(在瀏覽器中是window對(duì)象)中的

2.2.1 將構(gòu)造函數(shù)當(dāng)作函數(shù)
//當(dāng)作構(gòu)造函數(shù)使用
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName();   //"Nicholas"

//作為普通函數(shù)調(diào)用
Person("Greg", 27, "Doctor");   //嚴(yán)格模式下會(huì)拋出錯(cuò)誤!
window.sayName();   //"Greg"

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

使用構(gòu)造函數(shù)的主要問(wèn)題,就是每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍。在前面的例子中,person1person2都有一個(gè)名為sayName()的方法,但那兩個(gè)方法不是同一個(gè)Function的實(shí)例

不要忘了——ECMAScript中的函數(shù)是對(duì)象,因此每定義一個(gè)函數(shù),也就是實(shí)例化了一個(gè)對(duì)象。從邏輯角度講,此時(shí)的構(gòu)造函數(shù)也可以這樣定義:

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = new Function("console.log(this.name);");    //此聲明函數(shù)在邏輯上是等價(jià)的    
}

console.log(person1.sayName == person2.sayName);    //false

創(chuàng)建兩個(gè)完成同樣任務(wù)的Function實(shí)例的確沒(méi)有必要;況且有this對(duì)象在,根本不用在執(zhí)行代碼前就把函數(shù)綁定到特定對(duì)象上。于是可以將函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)外部來(lái)解決這個(gè)問(wèn)題:

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

function sayName() {
  console.log(this.name);
};

這樣做解決了兩個(gè)函數(shù)做同一件事的問(wèn)題,可是新問(wèn)題又來(lái)了:在全局作用域中定義的函數(shù)實(shí)際上只能被某個(gè)對(duì)象調(diào)用,這讓全局作用域有點(diǎn)名不副實(shí)。更重要的是:如果對(duì)象需要定義很多方法,那么就要定義很多個(gè)全局函數(shù),于是我們這個(gè)自定義的引用類(lèi)型就絲毫沒(méi)有封裝性可言了。好在,原型模式可以解決這些問(wèn)題

2.3 原型模式
function Person() {
}

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

var person1 = new Person();
person1.sayName();    //"Nicholas"

var person2 = new Person();
person2.sayName();    //"Nicholas"

console.log(person1.sayName == person2.sayName);    //true
2.3.1 理解原型對(duì)象

各個(gè)對(duì)象之間的關(guān)系

console.log(Person.prototype.isPrototypeOf(person1));   //true
console.log(Person.prototype.isPrototypeOf(person2));   //true

ECMAScript 5增加了一個(gè)新方法,叫Object.getPrototypeOf(),這個(gè)方法返回[[Prototype]]的值

console.log(Object.getPrototypeOf(person1) == Person.prototype);    //true
console.log(Object.getPrototypeOf(person1).name);   //"Nicholas"

不能通過(guò)對(duì)象實(shí)例重寫(xiě)原型中的值。如果在實(shí)例中添加一個(gè)屬性,而該屬性與實(shí)例原型中的一個(gè)屬性同名,那么該屬性將會(huì)屏蔽原型中的那個(gè)屬性:

function Person() {
}

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

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

console.log(person1.hasOwnProperty("name"));    //false
console.log("name" in person1);    //true

person1.name = "Greg";
console.log(person1.name);    //"Greg"——來(lái)自實(shí)例
console.log(person1.hasOwnProperty("name"));    //true
console.log("name" in person1);    //true

console.log(person2.name);    //"Nicholas"——來(lái)自原型
console.log(person2.hasOwnProperty("name"));    //false

delete person1.name;
console.log(person1.name);    //"Nicholas"——來(lái)自原型

in操作符只要通過(guò)對(duì)象能夠訪問(wèn)到屬性就返回true

使用hasOwnProperty()方法可以檢測(cè)一個(gè)屬性是存在于實(shí)例中,還是存在于原型中。這個(gè)方法(不要忘了它是從Object繼承來(lái)的)只在給定屬性存在于對(duì)象實(shí)例中時(shí),才會(huì)返回true

function hasPrototypeProperty(object, name) {
  return !object.hasOwnProperty(name) && (name in object);
}
  //返回true則表明該屬性存在于原型中
  //返回false則表明該屬性存在于實(shí)例中

在使用for-in循環(huán)時(shí),返回的是所有能夠通過(guò)對(duì)象訪問(wèn)的、可枚舉的屬性,其中包括實(shí)例和原型中的屬性。

ECMAScript 5Object.keys()方法可以取得對(duì)象上所有可枚舉的實(shí)例屬性

var keys = Object.keys(Person.prototype);
alert(keys);    //"name, age, job, sayName"
//keys中保存一個(gè)數(shù)組,數(shù)組中是字符串"name, age, job, sayName"。

Object.getownPropertyNames()可以獲得所有實(shí)例屬性,無(wú)論它是否可枚舉

var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys);    //"constructor, name, age, job, sayName"
2.3.3 更簡(jiǎn)單的原型語(yǔ)法
function Person() {
}

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

上面代碼將Person.prototype設(shè)置為等于一個(gè)以對(duì)象字面量形式創(chuàng)建的新對(duì)象。結(jié)果相同,但constructor屬性不再指向Person了。因此上面使用的語(yǔ)法,本質(zhì)上完全重寫(xiě)了默認(rèn)的prototype對(duì)象,因此constructor屬性也就變成了新對(duì)象的constructor屬性(指向Object構(gòu)造函數(shù)),不再指向Person函數(shù)

var friend = new Person();
console.log(friend instanceof Object);    //true
console.log(friend instanceof Person);    //true
console.log(friend.constructor == Object);    //false
console.log(friend.constructor == Person);    //true

如果constructor的值真的很重要,可以像下面這樣特意將它設(shè)置回恰當(dāng)?shù)闹担?/p>

function Person() {
}

Person.prototype = {
  constructor : Person
  //……
};

這種方式重設(shè)constructor屬性會(huì)導(dǎo)致它的[[Enumerable]]特性被設(shè)置為true,默認(rèn)情況下,原聲的constructor屬性是不可枚舉的

因此如果你使用兼容ECMAScript 5JavaScript引擎,可以試一試Object.defineProperty()

Object.defineProperty(Person.prototype, "constructor", {
  enumerable : false,
  value : Person
});
2.3.4 原型的動(dòng)態(tài)性

在原型中查找值的過(guò)程是一次搜索

var friend = new Person();
Person.prototype.sayHi = function(){
    console.log("hi");
}

friend.sayHi();    //"hi" (沒(méi)有問(wèn)題!)

重寫(xiě)原型對(duì)象:

function Person(){
}

var friend = new Person();
Person.prototype = {
  constructor : Person,
  name : "Nicholas",
  age : 29,
  job : "Software Engineer",
  sayName : function () {
    console.log(this.name);
  }
};
friend.sayName();   //error

重寫(xiě)原型對(duì)象

重寫(xiě)原型對(duì)象切斷了現(xiàn)有原型與任何之前已經(jīng)存在的對(duì)象實(shí)例之間的聯(lián)系;它們引用的仍然是最初的原型

2.3.5 原生對(duì)象的原型

所有原生引用類(lèi)型(Object、Array、String等)都在其構(gòu)造函數(shù)的原型上定義了方法

console.log(typeof Array.prototype.sort);    //"function"
console.log(typeof String.prototype.substring);    //"function"

給原生對(duì)象的原型添加方法:

String.prototype.startsWith = function(text){
    return this.indexOf(text) == 0;
}

var msg = "Hello world!";
console.log(msg.startsWith("Hello"));    //true
2.3.6 原型對(duì)象的問(wèn)題

對(duì)于包含引用類(lèi)型值得屬性來(lái)說(shuō),可能出現(xiàn)以下問(wèn)題:

function Person() {
}

Person.prototype = {
  constructor : Person,
  name : "Nicholas",
  age : 29,
  job : "Software Engineer",
  friends : ["Shelby", "Court"],
  sayName : function () {
    console.log(this.name);
  }
};

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

person1.friends.push("Van");

console.log(person1.friends);
console.log(person2.friends);
console.log(person1.friends === person2.friends);   //true
2.4 組合使用構(gòu)造函數(shù)模式和原型模式

實(shí)例屬性都在構(gòu)造函數(shù)中定義,所有實(shí)例共享的屬性和方法都在原型中定義:

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

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

這種構(gòu)造函數(shù)與原型混成的模式,是目前在ECMAScript中使用最廣泛、認(rèn)同度最高的一種創(chuàng)建自定義類(lèi)型的方法。可以說(shuō),這是用來(lái)定義引用類(lèi)型的一種默認(rèn)模式

2.5 動(dòng)態(tài)原型模式

有其他OO語(yǔ)言經(jīng)驗(yàn)的開(kāi)發(fā)人員在看到獨(dú)立的構(gòu)造函數(shù)和原型時(shí),很可能會(huì)感到非常困惑。動(dòng)態(tài)原型模式正式致力于解決這個(gè)問(wèn)題的一個(gè)方案

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

  //方法
  if(typeof this.sayName != "function"){
    Person.prototype.sayName = function () {
      console.log(this.name);
    }
  }
}

if語(yǔ)句檢查的可以是初始化之后應(yīng)該存在的任何屬性和方法——不必用一大堆if語(yǔ)句檢查每個(gè)屬性和每個(gè)方法;只要檢查其中一個(gè)即可。對(duì)于采用這種模式的對(duì)象,還可以使用instanceof操作符確定它的類(lèi)型

2.6 寄生構(gòu)造函數(shù)模式

通常,在前述的幾種模式都不適用的情況下,可以使用寄生構(gòu)造函數(shù)模式。這種模式的基本思想是創(chuàng)建一個(gè)函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對(duì)象的代碼,然后再返回新創(chuàng)建的對(duì)象;但從表面上看,這個(gè)函數(shù)又很像是典型的構(gòu)造函數(shù):

function Person(name, age, job) {
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function () {
    console.log(this.name);
  };
  return o;
}

var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();   //"Nicholas"

構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會(huì)返回新對(duì)象實(shí)例。而通過(guò)在構(gòu)造函數(shù)的末尾添加一個(gè)return語(yǔ)句,可以重寫(xiě)調(diào)用構(gòu)造函數(shù)時(shí)返回的值。

這個(gè)模式可以在特殊的情況下用來(lái)為對(duì)象創(chuàng)建構(gòu)造函數(shù)。假設(shè)我們想創(chuàng)建一個(gè)具有額外方法的特殊數(shù)組。由于不能直接修改Array構(gòu)造函數(shù),因此可以使用這個(gè)模式:

function SpecialArray() {
  //創(chuàng)建數(shù)組
  var values = new Array();
  //添加值
  values.push.apply(values, arguments);
  //添加方法
  values.toPipedString = function () {
    return this.join("|");
  }
  //返回?cái)?shù)組
  return values;
}

var colors = new SpecialArray("red", "blue", "green");
console.log(colors.toPipedString());    //"red|blue|green"

注意:返回的對(duì)象與構(gòu)造函數(shù)或者與構(gòu)造函數(shù)的原型屬性直接沒(méi)有關(guān)系;也就是說(shuō),構(gòu)造函數(shù)返回的對(duì)象與在構(gòu)造函數(shù)外部創(chuàng)建的對(duì)象沒(méi)有什么不同。為此,不能依賴(lài)instanceof操作符來(lái)確定對(duì)象類(lèi)型。

由于存在上述問(wèn)題,建議在可以使用其他模式的情況下,不要使用這種模式。

2.7 穩(wěn)妥構(gòu)造函數(shù)模式

所謂穩(wěn)妥對(duì)象,指的是沒(méi)有公共屬性,而且其方法也不引用this的對(duì)象。穩(wěn)妥對(duì)象最合適在一些安全的環(huán)境中(這些環(huán)境中會(huì)禁止使用thisnew),或者在防止數(shù)據(jù)被其他應(yīng)用程序(如Mashup程序)改動(dòng)時(shí)使用。

function Person(name, age, job) {
  //創(chuàng)建要返回的對(duì)象
  var o = new Object();
  //可以在這里定義私有變量和函數(shù)

  //添加方法
  o.sayName = function () {
    console.log(name);
  }

  return o;
}

var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName();   //"Nicholas"

變量friend中保存的是一個(gè)穩(wěn)妥對(duì)象,而除了調(diào)用sayName()方法外,沒(méi)有別的方式可以訪問(wèn)其數(shù)據(jù)成員。即使有其他代碼會(huì)給這個(gè)對(duì)象添加方法或數(shù)據(jù)成員,但也不可能有別的辦法訪問(wèn)傳入到構(gòu)造函數(shù)中的原始數(shù)據(jù)。

穩(wěn)妥構(gòu)造函數(shù)模式提供的這種安全性,使得它非常適合在某些安全執(zhí)行環(huán)境下使用——例如,ADsafeCaja提供的環(huán)境

3 繼承

許多OO語(yǔ)言都支持兩種繼承方式:接口繼承和實(shí)現(xiàn)繼承。接口繼承只繼承方法簽名,而實(shí)現(xiàn)繼承則繼承實(shí)際的方法。由于函數(shù)沒(méi)有簽名,在ECMAScript中無(wú)法實(shí)現(xiàn)接口繼承。ECMAScript只支持實(shí)現(xiàn)繼承,而且其實(shí)現(xiàn)繼承主要是依靠原型鏈來(lái)實(shí)現(xiàn)的

3.1 原型鏈
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();
console.log(instance.getSuperValue());    //true

通過(guò)原型鏈繼承

3.1.1 別忘記默認(rèn)的原型

所有引用類(lèi)型默認(rèn)都繼承了Object,而這個(gè)繼承也是通過(guò)原型鏈實(shí)現(xiàn)的。
默認(rèn)原型Object

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

instanceof:只要用這個(gè)操作符來(lái)測(cè)試實(shí)例與原型鏈中出現(xiàn)過(guò)的構(gòu)造函數(shù),結(jié)果就會(huì)返回true

console.log(instance instanceof Object);      //true
console.log(instance instanceof SuperType);   //true
console.log(instance instanceof SubType);     //true

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

console.log(Object.prototype.isPrototypeOf(instance));      //true
console.log(SuperType.prototype.isPrototypeOf(instance));   //true
console.log(SubType.prototype.isPrototypeOf(instance));     //true
3.1.3 謹(jǐn)慎地定義方法

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

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;
};

//重寫(xiě)超類(lèi)型中的方法
SubType.prototype.getSuperValue = function(){
    return false;
}

var instance = new SubType();
console.log(instance.getSuperValue());    //false

getSuperValue()是原型鏈中已經(jīng)存在的一個(gè)方法,重寫(xiě)這個(gè)方法將會(huì)屏蔽原來(lái)的那個(gè)方法。當(dāng)通過(guò)SubType的實(shí)例調(diào)用getSuperValue()時(shí),調(diào)用的就是這個(gè)重新定義的方法;但通過(guò)SuperType的實(shí)例調(diào)用getSuperValue()時(shí),還會(huì)繼續(xù)調(diào)用原來(lái)的那個(gè)方法

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

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

SuperType.prototype.getSuperValue = function () {
  return this.property;
}

function SubType() {
  this.subProperty = false;
}

//繼承了SuperType
SubType.prototype = new SuperType();

//使用字面量添加新方法,會(huì)導(dǎo)致上一行代碼無(wú)效
SubType.prototype = {
    //……
}

var instance = new SubType();
console.log(instance.getSuperValue());    //error
3.1.4 原型鏈的問(wèn)題

包含引用類(lèi)型值的原型會(huì)被所有實(shí)例共享。在通過(guò)原型來(lái)實(shí)現(xiàn)繼承時(shí),原型實(shí)際上會(huì)變成另一個(gè)類(lèi)型的實(shí)例。于是,原先的實(shí)例屬性也就變成了現(xiàn)在的原型屬性了

function SuperType(){
  this.colors = ["red", "blue", "green"];
}

function SubType(){
}

//繼承了SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);    //"red,blue,green,black"

var instance2 = new SubType();
console.log(instance2.colors);    //"red,blue,green,black"

SubType的所有實(shí)例都會(huì)共享這一個(gè)colors屬性。

原型鏈的第二個(gè)問(wèn)題:在創(chuàng)建子類(lèi)型的實(shí)例時(shí),不能向超類(lèi)型的構(gòu)造函數(shù)中傳遞參數(shù)。實(shí)際上,應(yīng)該說(shuō)是沒(méi)有辦法在不影響所有對(duì)象實(shí)例的情況下,給超類(lèi)型的構(gòu)造函數(shù)傳遞參數(shù)。

3.2 借用構(gòu)造函數(shù)

在子類(lèi)型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類(lèi)型構(gòu)造函數(shù)

function SuperType(){
  this.colors = ["red", "blue", "green"];
}

function SubType(){
  //繼承了SuperType
  SuperType.call(this);
}

var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);    //"red,blue,green,black"

var instance2 = new SubType();
console.log(instance2.colors);    //"red,blue,green"
3.2.1 傳遞參數(shù)
function SuperType(name){
  this.name = name;
} 

function SubType(){
  //繼承了SuperType,同時(shí)還傳遞了參數(shù)
  SuperType.call(this, "Nicholas");

  //實(shí)例屬性
  this.age = 29;
}

var instance = new SubType();
console.log(instance.name);   //"Nicholas";
console.log(instance.age);    //29

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

3.3 組合繼承

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

function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function (){
  console.log(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 (){
  console.log(this.age);
}

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors);    //["red", "blue", "green", "black"]
instance1.sayName();    //Nicholas
instance1.sayAge();     //29

var instance2 = new SubType("Greg", 27);
console.log(instance2.colors);   //["red", "blue", "green"]
instance2.sayName();    //Greg
instance2.sayAge();     //27
3.4 原型式繼承

原型式繼承并沒(méi)有使用嚴(yán)格意義上的構(gòu)造函數(shù)。他的想法是借助原型可以基于已有的對(duì)象創(chuàng)建新對(duì)象,同時(shí)還不必因此創(chuàng)建自定義類(lèi)型

function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}

var person = {
  name : "Nicholas",
  friends : ["Shelby", "Court", "Van"]
};

var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

console.log(person.friends);    //["Shelby", "Court", "Van", "Rob", "Barbie"]

object()函數(shù)內(nèi)部,先創(chuàng)建了一個(gè)臨時(shí)性的構(gòu)造函數(shù),然后將傳入的對(duì)象作為這個(gè)構(gòu)造函數(shù)的原型,最后返回了這個(gè)臨時(shí)類(lèi)型的一個(gè)新實(shí)例。從本質(zhì)上講,object()隊(duì)傳入其中的對(duì)象執(zhí)行了一次淺復(fù)制

ECMAScript 5通過(guò)新增Object.create()方法規(guī)范化了原型式繼承:

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

console.log(person.friends);    //["Shelby", "Court", "Van", "Rob", "Barbie"]

在傳入一個(gè)參數(shù)的情況下,Object.create()object()方法的行為相同

var person = {
  name : "Nicholas",
  friends : ["Shelby", "Court", "Van"]
};

var anotherPerson = Object.create(person, {
    name : {
        value : "Greg"
    }
});

console.log(anotherPerson.name);    //"Greg"

Object.create()方法的第二個(gè)參數(shù)與Object.defineProperties()方法的第二個(gè)參數(shù)格式相同:每個(gè)屬性都是通過(guò)自己的描述符定義的。以這種方式指定的任何屬性都會(huì)覆蓋原型對(duì)象上的同名屬性

在沒(méi)有必要興師動(dòng)眾地創(chuàng)建構(gòu)造函數(shù),而只想讓一個(gè)對(duì)象與另一個(gè)對(duì)象保持類(lèi)似的情況下,原型試?yán)^承時(shí)完全可以勝任的。不過(guò)別忘了,包含引用類(lèi)型值的屬性始終都會(huì)共享相應(yīng)的值,就像使用原型模式一樣。

3.5 寄生式繼承

寄生式繼承是與原型試?yán)^承緊密相關(guān)的一種思路。寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類(lèi)似,即創(chuàng)建一個(gè)僅用于封裝繼承過(guò)程的函數(shù),該函數(shù)在內(nèi)部以某種方式來(lái)增強(qiáng)對(duì)象,最后再像真的是它做了所有工作一樣返回對(duì)象。一下代碼規(guī)范了寄生式繼承模式:

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

可以像下面這樣來(lái)使用createAnother()函數(shù):

var person = {
  name : "Nicholas",
  friends : ["Shelby", "Court", "Van"]
};

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

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

在主要考慮對(duì)象而不是自定義類(lèi)型和構(gòu)造函數(shù)的情況下,寄生式繼承也是一種有用的模式。前面示范繼承模式時(shí)使用的object()函數(shù)不是必須的;任何能夠返回新對(duì)象的函數(shù)都適用于此模式

3.6 寄生組合式繼承

通過(guò)借用構(gòu)造函數(shù)來(lái)繼承屬性,通過(guò)原型鏈的混成形式來(lái)繼承方法
寄生組合式繼承的基本模式如下:

function inheritPrototype(subType, superType){
  var prototype = object(superType.prototype);    //創(chuàng)建對(duì)象
  prototype.constructor = subType;    //增強(qiáng)對(duì)象
  subType.prototype = prototype;    //指定對(duì)象
}
function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function (){
  console.log(this.name);
};

function SubType(name, age){
  SuperType.call(this, name);
  this.age = age;
}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function (){
  console.log(this.age);
};

這個(gè)例子的高效率體現(xiàn)在它只調(diào)用了一次SuperType構(gòu)造函數(shù),并且因此避免了再SubType.prototype上面創(chuàng)建不必要的、多余的屬性。與此同時(shí),原型鏈還能保持不變;因此,還能夠正常使用instanceofisPrototypeOf()

開(kāi)發(fā)人員普遍認(rèn)為寄生組合式繼承時(shí)引用類(lèi)型最理想的繼承方式。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/82806.html

相關(guān)文章

  • 高程讀書(shū)筆記 六章 面向對(duì)象程序設(shè)計(jì)

    摘要:創(chuàng)建一個(gè)新對(duì)象將構(gòu)造函數(shù)的作用域賦給新對(duì)象因此就指向了這個(gè)新對(duì)象執(zhí)行構(gòu)造函數(shù)中的代碼為這個(gè)新對(duì)象添加屬性返回新對(duì)象。 本章內(nèi)容 理解對(duì)象屬性 理解并創(chuàng)建對(duì)象 理解繼承 ECMA-262把對(duì)象定義為:無(wú)序?qū)傩缘募希鋵傩钥梢园局怠?duì)象或者函數(shù) 理解對(duì)象 創(chuàng)建對(duì)象 創(chuàng)建自定義對(duì)象的最簡(jiǎn)單方式就是創(chuàng)建一個(gè)Object的實(shí)例,再為它添加屬性和方法。 var person = new...

    468122151 評(píng)論0 收藏0
  • JS高程讀書(shū)筆記--六章原型繼承

    摘要:高程讀書(shū)筆記第六章理解對(duì)象創(chuàng)建自定義對(duì)象的方式有創(chuàng)建一個(gè)實(shí)例,然后為它添加屬性和方法。創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對(duì)象默認(rèn)只會(huì)取得屬性至于其他方法都是從繼承而來(lái)的。 JS高程讀書(shū)筆記--第六章 理解對(duì)象 創(chuàng)建自定義對(duì)象的方式有創(chuàng)建一個(gè)Object實(shí)例,然后為它添加屬性和方法。還可用創(chuàng)建對(duì)象字面量的方式 屬性類(lèi)型 ECMAScript在定義只有內(nèi)部采用的特性時(shí),描述了屬性的各種特征...

    EasonTyler 評(píng)論0 收藏0
  • JavaScript高程六章:繼承-理解與實(shí)踐

    摘要:高程第六章繼承理解與實(shí)踐昨日細(xì)細(xì)的讀了一遍高程現(xiàn)在寫(xiě)篇文章來(lái)鞏固下認(rèn)知吧讀首先是從中讀到了什么我自己也在讀書(shū)的時(shí)候用筆記下了各個(gè)部分的點(diǎn)現(xiàn)在等于閱讀筆記回憶下書(shū)本理解基礎(chǔ)第五版中規(guī)定了兩種屬性數(shù)據(jù)屬性訪問(wèn)器屬性數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位 JavaScript高程第六章:繼承-理解與實(shí)踐昨日細(xì)細(xì)的讀了一遍JavaScript高程,現(xiàn)在寫(xiě)篇文章來(lái)鞏固下認(rèn)知吧. 讀 首先是從中讀到了什么,我...

    mozillazg 評(píng)論0 收藏0
  • 《Javascript高級(jí)程序設(shè)計(jì) (第三版)》六章 面向對(duì)象程序設(shè)計(jì)

    摘要:把原型修改為另外一個(gè)對(duì)象就等于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系。組合使用構(gòu)造函數(shù)模式動(dòng)態(tài)原型模式通過(guò)檢查某個(gè)應(yīng)該存在的方法是否有效,來(lái)決定是否需要初始化原型。 理解對(duì)象 屬性類(lèi)型 數(shù)據(jù)屬性 數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位置。在這個(gè)位置可以讀取和寫(xiě)入值。數(shù)據(jù)屬性有 4 個(gè)描述其行為的特性。 [[Configurable]] :表示能否通過(guò) delete 刪除屬性從而重新定義屬性,能否修...

    yy736044583 評(píng)論0 收藏0
  • 《javascript高級(jí)程序設(shè)計(jì)六章 讀書(shū)筆記 之 javascript對(duì)象幾種創(chuàng)建方式

    摘要:三種使用構(gòu)造函數(shù)創(chuàng)建對(duì)象的方法和的作用都是在某個(gè)特殊對(duì)象的作用域中調(diào)用函數(shù)。這種方式還支持向構(gòu)造函數(shù)傳遞參數(shù)。叫法上把函數(shù)叫做構(gòu)造函數(shù),其他無(wú)區(qū)別適用情境可以在特殊的情況下用來(lái)為對(duì)象創(chuàng)建構(gòu)造函數(shù)。 一、工廠模式 工廠模式:使用字面量和object構(gòu)造函數(shù)會(huì)有很多重復(fù)代碼,在此基礎(chǔ)上改進(jìn)showImg(https://segmentfault.com/img/bVbmKxb?w=456&...

    xiaotianyi 評(píng)論0 收藏0

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

0條評(píng)論

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