首頁 > web前端 > js教程 > javascript繼承的六大模式小結_javascript技巧

javascript繼承的六大模式小結_javascript技巧

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
發布: 2016-05-16 16:04:36
原創
1173 人瀏覽過

1.原型鏈

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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

登入後複製

實現的本質是重寫原型對象,並以一個新類型的實例取代。

2.借用建構子

1

2

3

4

5

6

7

8

9

10

11

12

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"

登入後複製

如果只是藉用構造函數,那麼也將無法避免構造函數模式存在的問題——方法都在構造函數中定義,因此函數復用就無從談起了。而且,在超類型的原型中定義的方法,對子類型而言也是不可見的,結果所有型別都只能使用建構函式模式。考慮到這些問題,借用構造函數的技術也是很少單獨使用的。

3.組合繼承

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

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

登入後複製

在這個範例中,SuperType 建構子定義了兩個屬性:name 和colors。 SuperType 的原型定義了一個方法sayName()。 SubType 建構子在呼叫SuperType 建構子時傳入了name 參數,緊接著又定義了它自己的屬性age。然後,將SuperType 的實例賦值給SubType 的原型,然後在該新原型上定義了方法sayAge()。這樣一來,就可以讓兩個不同的SubType 實例既分別擁有自己屬性──包含colors 屬性,又可以使用相同的方法了。

組合繼承避免了原型鍊和借用建構函式的缺陷,融合了它們的優點,成為JavaScript 中最常用的繼承模式。而且,instanceof 和isPrototypeOf()也能夠用來識別基於組合繼承所建立的物件。

4.原型式繼承

1

2

3

4

5

function object(o){

function F(){}

F.prototype = o;

return new F();

}

登入後複製

在object()函數內部,先建立了一個臨時性的建構函數,然後將傳入的物件作為這個建構函數的原型,最後傳回了這個臨時型別的一個新實例。從本質上講,object()對傳入其中的物件執行了一次淺複製。來看下面的例子。

1

2

3

4

5

6

7

8

9

10

11

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

alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

登入後複製

克羅克福德主張的這種原型式繼承,要求你必須有一個物件可以作為另一個物件的基礎。如果有這麼一個物件的話,可以把它傳遞給object()函數,然後再根據具體需求對得到的物件加以修改即可。在這個例子中,可以作為另一個物件基礎的是person 對象,於是我們把它傳入到object()函數中,然後函數就會傳回一個新物件。這個新物件將person 作為原型,所以它的原型中就包含一個基本類型值屬性和一個引用類型值屬性。這意味著person.friends 不僅屬於person 所有,而且也會被anotherPerson以及yetAnotherPerson 共享。實際上,這就相當於又建立了person 物件的兩個副本。

ECMAScript 5 透過新增Object.create()方法規範化了原型式繼承。這個方法接收兩個參數:一個用作新物件原型的物件和(可選的)一個為新物件定義額外屬性的物件。在傳入一個參數的情況下,Object.create()與object()方法的行為相同。

1

2

3

4

5

6

7

8

9

10

11

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"

登入後複製

Object.create()方法的第二個參數與Object.defineProperties()方法的第二個參數格式相同:每個屬性都是透過自己的描述子定義的。以這種方式指定的任何屬性都會覆寫原型物件上的同名屬性。例如:

1

2

3

4

5

6

7

8

9

10

11

var person = {

name: "Nicholas",

friends: ["Shelby", "Court", "Van"]

};

 

var anotherPerson = Object.create(person, {

name: {

value: "Greg"

}

});

alert(anotherPerson.name); //"Greg"

登入後複製

支援Object.create()方法的瀏覽器有IE9 、Firefox 4 、Safari 5 、Opera 12 和Chrome。

在沒有必要興師動眾地創建構造函數,而只想讓一個物件與另一個物件保持類似的情況下,原型式繼承是完全可以勝任的。不過別忘了,包含引用類型值的屬性總是會共用對應的值,就像使用原型模式一樣。

5.寄生式繼承

寄生式(parasitic)繼承是與原型式繼承緊密相關的一種思路,並且同樣也是由克羅克福德推而廣之的。寄生式繼承的想法與寄生構造函數和工廠模式類似,即創建一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來增強對象,最後再像真地是它做了所有工作一樣返回對象。以下程式碼示範了寄生式繼承模式。

1

2

3

4

5

6

7

function createAnother(original){

var clone = object(original); //通过调用函数创建一个新对象

clone.sayHi = function(){ //以某种方式来增强这个对象

alert("hi");

};

return clone; //返回这个对象

}

登入後複製

在這個例子中,createAnother()函數接收了一個參數,也就是將要作為新物件基礎的物件。然後,把這個物件(original)傳遞給object()函數,將傳回的結果賦值給clone。再為clone 物件新增一個方法sayHi(),最後回傳clone 物件。可以像下面這樣來使用createAnother()函數:

1

2

3

4

5

6

var person = {

name: "Nicholas",

friends: ["Shelby", "Court", "Van"]

};

var anotherPerson = createAnother(person);

anotherPerson.sayHi(); //"hi"

登入後複製

这个例子中的代码基于person 返回了一个新对象——anotherPerson。新对象不仅具有person的所有属性和方法,而且还有自己的sayHi()方法。
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前面示范继承模式时使用的object()函数不是必需的;任何能够返回新对象的函数都适用于此模式。

使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一
点与构造函数模式类似。

6.寄生组合式继承

前面说过,组合继承是JavaScript 最常用的继承模式;不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。再来看一看下面组合继承的例子。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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); //第二次调用SuperType()

this.age = age;

}

SubType.prototype = new SuperType(); //第一次调用SuperType()

SubType.prototype.constructor = SubType;

SubType.prototype.sayAge = function(){

alert(this.age);

};

登入後複製

加粗字体的行中是调用SuperType 构造函数的代码。在第一次调用SuperType 构造函数时,SubType.prototype 会得到两个属性:name 和colors;它们都是SuperType 的实例属性,只不过现在位于SubType 的原型中。当调用SubType 构造函数时,又会调用一次SuperType 构造函数,这一次又在新对象上创建了实例属性name 和colors。于是,这两个属性就屏蔽了原型中的两个同名属性。图6-6 展示了上述过程。
如图6-6 所示,有两组name 和colors 属性:一组在实例上,一组在SubType 原型中。这就是调用两次SuperType 构造函数的结果。好在我们已经找到了解决这个问题方法——寄生组合式继承。

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。寄生组合式继承的基本模式如下所示。

1

2

3

4

5

function inheritPrototype(subType, superType){

var prototype = object(superType.prototype); //创建对象

prototype.constructor = subType; //增强对象

subType.prototype = prototype; //指定对象

}

登入後複製

这个示例中的inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接收两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二步是为创建的副本添加constructor 属性,从而弥补因重写原型而失去的默认的constructor 属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我们就可以用调用inherit-Prototype()函数的语句,去替换前面例子中为子类型原型赋值的语句了,例如

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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;

}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function(){

alert(this.age);

};

登入後複製

这个例子的高效率体现在它只调用了一次SuperType 构造函数,并且因此避免了在SubType.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceof 和isPrototypeOf()。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

YUI 的YAHOO.lang.extend()方法采用了寄生组合继承,从而让这种模式首次出现在了一个应用非常广泛的JavaScript 库中。要了解有关YUI 的更多信息,请访问http://developer. yahoo.com/yui/。

以上所述就是本文的全部内容了,希望对大家学习javascript继承有所帮助。

相關標籤:
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板