學(xué)習(xí)JavaScript設(shè)計(jì)模式(繼承)
1、繼承
在javascript中繼承是一個(gè)非常復(fù)雜的話題,比其他任何面向?qū)ο笳Z(yǔ)言的中的繼承都復(fù)雜得多。在大多數(shù)其他面向?qū)ο笳Z(yǔ)言中,繼承一個(gè)類(lèi)只需要使用一個(gè)關(guān)鍵字即可。與它們不同,在javascript中要想達(dá)到傳承公用成員的目的,需要采取一系列措施。更有甚者,javascript屬于使用原型式繼承的少數(shù)語(yǔ)言之一。利益于這種語(yǔ)言的靈活性,你既可使用標(biāo)準(zhǔn)的基于類(lèi)的繼承,也可使用更微妙一些的原型式繼承。
2、為什么需要繼承?
一般來(lái)說(shuō),在設(shè)計(jì)類(lèi)的時(shí)候,我們希望能減少重復(fù)性的代碼,并且盡量弱化對(duì)象間的耦合。使用繼承符合前一個(gè)設(shè)計(jì)原則的需要。借助這種機(jī)制,你可以在現(xiàn)有類(lèi)的基礎(chǔ)上進(jìn)行設(shè)計(jì)并充分利用它們已經(jīng)具備的各種方法,而對(duì)設(shè)計(jì)進(jìn)行修改也更為輕松。假設(shè)你需要讓幾個(gè)類(lèi)都擁有一個(gè)按特定方式輸出類(lèi)結(jié)構(gòu)的toString()方法,當(dāng)然可以用復(fù)制加粘貼的辦法把定義toString()方法的代碼添加到每一個(gè)類(lèi)中,但這樣做的話,每當(dāng)需要改變這個(gè)方法的工作方式時(shí),你將不得不在每一個(gè)類(lèi)中重復(fù)同樣的修改。反之,如果你提供了一個(gè)ToStringProvider類(lèi),然后讓那些類(lèi)繼承這個(gè)類(lèi),那么toString這個(gè)方法只需在一個(gè)地方聲明即可。
讓一個(gè)類(lèi)繼承另一個(gè)類(lèi)可能會(huì)導(dǎo)致二者產(chǎn)生強(qiáng)耦合,也即一個(gè)類(lèi)的依賴(lài)于另一個(gè)類(lèi)的內(nèi)部實(shí)現(xiàn)。我們將討論一些有助于避免這種問(wèn)題的技術(shù),其中包括用摻元類(lèi)為其他類(lèi)提供方法這種技術(shù)。
3、基于類(lèi)的繼承
下面看下面的代碼:
<script type="text/javascript">
function Person(name, age)
{
this.name = name;
this.age = age;
}
Person.prototype.say = function ()
{
console.log(this.name + " , " + this.age);
}
function Student(no)
{
this.no = no;
}
/**
* Student的prototype指向Person的對(duì)象
*/</span>
Student.prototype = new Person();
var stu1 = new Student("0001");
stu1.name = '張三';
stu1.age = '11';
console.log(stu1.no);
stu1.say();
</script>
輸出結(jié)果:
張三 , 11
可以看到Student成功集成了Person,并且擁有了Person的say方法,核心代碼其實(shí)就是一句 Student.prototype = new Person();,下面通過(guò)圖解來(lái)說(shuō)明原理:

將Student.prototype指向new Person() , new Person的_proto_又指向Person Prototype;這樣完成了整個(gè)繼承。
但是這種方式存在問(wèn)題:
問(wèn)題1:當(dāng)父類(lèi)存在引用類(lèi)型變量時(shí),造成數(shù)據(jù)不一致,下面我們給Person添加一個(gè)hobbies屬性,類(lèi)型為數(shù)組。
<script type="text/javascript">
/**
* 存在問(wèn)題
* 1、無(wú)法在Student的構(gòu)造方法中傳遞參數(shù)用于父類(lèi)的構(gòu)造方法
* 2、對(duì)于引用類(lèi)型變量,造成數(shù)據(jù)不一致
*/
function Person(name, age)
{
this.name = name;
this.age = age;
this.hobbies = [] ;
}
Person.prototype.say = function ()
{
console.log(this.name + " , " + this.age +" , " +this.hobbies);
}
function Student(no)
{
this.no = no;
}
Student.prototype = new Person();
var stu1 = new Student("0001");
stu1.name = '張三';
stu1.age = '11';
stu1.hobbies.push("soccer");
stu1.say();
var stu2 = new Student("0002");
stu2.name = '李四';
stu2.age = '12';
stu2.hobbies.push("girl");
stu2.say();
</script>
輸出結(jié)果:
張三 , 11 , soccer
李四 , 12 , soccer,girl
可以看出,李四的hobbies應(yīng)該只有g(shù)irl,但是上面的代碼讓所有對(duì)象共享了hobbies屬性。
上述的繼承方式還存在一個(gè)問(wèn)題:
問(wèn)題2:在Student的構(gòu)造方法中,無(wú)法使用new Student(“00001” , “張三” , 12) ;創(chuàng)建對(duì)象,并初始化name和age屬性,必須stu.name, stu.age進(jìn)行賦值
為了解決上述問(wèn)題,對(duì)上述代碼進(jìn)行修改:
<script type="text/javascript">
function Person(name, age)
{
this.name = name;
this.age = age;
this.hobbies = [];
}
Person.prototype.say = function ()
{
console.log(this.name + " , " + this.age +" , " + this.hobbies);
}
function Student(name, age, no)
{
/**
* 使用call方法,第一個(gè)參數(shù)為上下文;
* 有點(diǎn)類(lèi)似Java中的super(name,age)的感覺(jué)
*/
Person.call(this, name, age);
this.no = no;
}
Student.prototype = new Person();
var stu1 = new Student("0001","張三",11);
stu1.hobbies.push("soccer");
stu1.say();
var stu2 = new Student("0002","李四",12);
stu2.hobbies.push("cangjin");
stu2.hobbies.push("basketball");
stu2.say();
</script>
輸出:
0001 , 張三 , soccer
0002 , 李四 , cangjin,basketball
在Student的構(gòu)造方法中使用了Person.call(this,name,age)感覺(jué)就像super(name,age)【call的第一個(gè)參數(shù)為上下文】;并且成功解決了對(duì)引用屬性的共享問(wèn)題,完美解決。
4、基于原型鏈的繼承
<script type="text/javascript">
/**
* 基于原型鏈的集成中都是對(duì)象
* 存在問(wèn)題:
* 1、對(duì)于引用類(lèi)型變量,造成數(shù)據(jù)不一致
*/
var Person = {
name: "人",
age: 0,
hobbies: [],
say: function ()
{
console.log(this.name + " , " + this.age + " , " + this.hobbies);
}
}
;
var Student = clone(Person);
Student.no ="";
Student.sayHello = function()
{
console.log(this.name +"hello ") ;
}
var stu1 = clone(Student);
stu1.name = "zhangsan";
stu1.age = 12;
stu1.hobbies.push("Java");
stu1.say();
var stu2 = clone(Student);
stu2.name = "lisi";
stu2.age = 13;
stu2.hobbies.push("Javascript");
stu2.say();
/**
* 返回一個(gè)prototype執(zhí)行obj的一個(gè)對(duì)象
* @param obj
* @returns {F}
*/
function clone(obj)
{
var F = function ()
{
};
F.prototype = obj;
return new F();
}
</script>
輸出:
zhangsan , 12 , Java
lisi , 13 , Java,Javascript
可以看出同樣存在引用屬性不一致的問(wèn)題,并且整個(gè)操作全部基于對(duì)象,給人的感覺(jué)不是很好,下面通過(guò)圖解解釋下原理:

對(duì)象間通過(guò)一個(gè)clone函數(shù),不斷的返回一個(gè)新的對(duì)象,且prototype執(zhí)行傳入的對(duì)象,整個(gè)繼承過(guò)程其實(shí)就是_proto_不斷的指向,形成一個(gè)鏈,所以叫做原型鏈。
好了,已經(jīng)介紹完了,js的兩種集成的方式,最好使用的還是通過(guò)類(lèi)的繼承,比較穩(wěn)定。
以上就是關(guān)于繼承知識(shí)點(diǎn)的相關(guān)內(nèi)容介紹,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
javascript得到當(dāng)前頁(yè)的來(lái)路即前一頁(yè)地址的方法
這篇文章主要介紹了javascript得到當(dāng)前頁(yè)的來(lái)路即前一頁(yè)地址的方法,需要的朋友可以參考下2014-02-02
JS一維數(shù)組轉(zhuǎn)多維數(shù)組樹(shù)的方法
這篇文章主要介紹了JS一維數(shù)組轉(zhuǎn)多維數(shù)組樹(shù)的方法,文章通過(guò)代碼示例給大家講解的非常詳細(xì),?對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-06-06
JS自定義函數(shù)實(shí)現(xiàn)時(shí)間戳轉(zhuǎn)換成date的方法示例
這篇文章主要介紹了JS自定義函數(shù)實(shí)現(xiàn)時(shí)間戳轉(zhuǎn)換成date的方法,結(jié)合具體實(shí)例形式分析了javascript時(shí)間戳與日期格式的計(jì)算與轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2017-08-08
Echarts讀取動(dòng)態(tài)數(shù)據(jù)完整代碼
這篇文章主要給大家介紹了關(guān)于Echarts讀取動(dòng)態(tài)數(shù)據(jù)的相關(guān)資料,使用Echarts畫(huà)圖時(shí),數(shù)據(jù)一般不是靜態(tài)寫(xiě)死的,而是通過(guò)后端接口動(dòng)態(tài)獲取的,需要的朋友可以參考下2023-10-10
require.js與bootstrap結(jié)合實(shí)現(xiàn)簡(jiǎn)單的頁(yè)面登錄和頁(yè)面跳轉(zhuǎn)功能
這篇文章主要介紹了require.js與bootstrap結(jié)合實(shí)現(xiàn)簡(jiǎn)單的頁(yè)面登錄和頁(yè)面跳轉(zhuǎn)功能,需要的朋友可以參考下2017-05-05
js實(shí)現(xiàn)圖片和鏈接文字同步切換特效的方法
這篇文章主要介紹了js實(shí)現(xiàn)圖片和鏈接文字同步切換特效的方法,涉及javascript操作文字及圖片的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02

