溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JavaScript中繼承的用法

發(fā)布時(shí)間:2020-07-28 11:39:06 來(lái)源:億速云 閱讀:126 作者:小豬 欄目:web開(kāi)發(fā)

這篇文章主要講解了JavaScript中繼承的用法,內(nèi)容清晰明了,對(duì)此有興趣的小伙伴可以學(xué)習(xí)一下,相信大家閱讀完之后會(huì)有幫助。

正統(tǒng)的面相對(duì)象的語(yǔ)言都會(huì)提供extend之類(lèi)的方法用于出來(lái)類(lèi)的繼承,但Javascript并不提供extend方法,在Javascript中使用繼承需要用點(diǎn)技巧。

Javascript中的實(shí)例的屬性和行為是由構(gòu)造函數(shù)和原型兩部分組成的,我們定義兩個(gè)類(lèi):Person和zhangsan,它們?cè)趦?nèi)存中的表現(xiàn)如下圖1:
JavaScript中繼承的用法
如果想讓Zhangsan繼承Person,那么我們需要把Person構(gòu)造函數(shù)和原型中的屬性和行為全部傳給Zhangsan的構(gòu)造函數(shù)和原型,如下圖2所示:
JavaScript中繼承的用法

Are you Ok?了解了繼承的思路后,那么我們一步步完成Person和Zhangsan的繼承功能。首先,我們需要定義Person類(lèi),如下代碼:
[代碼1]

// 定義Person類(lèi)
function Person (name){
  this.name = name;
  this.type = "人";
}
Person.prototype={
  say : function(){
    console.info("我是一個(gè)"+ this.type +",我的名字叫" + this.name);
  }
}  
//定義Zhangsan類(lèi)
function Zhangsan (name){
}
Zhangsan.prototype={
  
}

Zhangsan雖然有自己特有的屬性和行為,但它大部分屬性和行為和Person相同,需要繼承自Person類(lèi)。如前所述,JavaScript中繼承是要分別繼承構(gòu)造函數(shù)和原型中的屬性和行為的。我們先讓Zhangsan繼承Person的構(gòu)造函數(shù)中的行為和屬性,如下代碼:
[代碼2]

// 定義Person類(lèi)
function Person (name){
  this.name = name;
  this.type = "黃";
}
Person.prototype={
  say : function(){
    console.info("我是一個(gè)"+ this.type +"種人,我的名字叫" + this.name);
  }
}  
//定義Zhangsan類(lèi)
function Zhangsan (name){
  this.name = name;
  this.type = "黃";
}
Zhangsan.prototype={
}
//實(shí)例化Zhangsan對(duì)象
var zs = new Zhangsan("張三");
console.info(zs.type);  // 黃

運(yùn)行正常,但我們?cè)趺礇](méi)看到繼承的“味道”呢?我們?cè)赯hangsan的構(gòu)造函數(shù)中將Person的屬性和行為復(fù)制了一份,與其說(shuō)是繼承不如說(shuō)是“真巧,這兩個(gè)類(lèi)的構(gòu)造函數(shù)除了函數(shù)名不同,其他地方都長(zhǎng)得一樣”。她的缺點(diǎn)很明顯:如果Person類(lèi)的構(gòu)造函數(shù)有任何變動(dòng),我們也需要手動(dòng)的同步修改Zhangsan類(lèi)的構(gòu)造函數(shù),同樣一份代碼,我們復(fù)制了一份寫(xiě)在了程序中 的不同地方,這違法了DRY原則,降低了代碼的可維護(hù)性。

好了,讓我們來(lái)改進(jìn)它:
[代碼3]

// 定義Person類(lèi)
function Person (name){
  this.name = name;
  this.type = "黃";
}
Person.prototype={
  say : function(){
    console.info("我是一個(gè)"+ this.type +"種人,我的名字叫" + this.name);
  }
}  
// 定義Zhangsan類(lèi)
function Zhangsan (name){
  Person(name);
}
Zhangsan.prototype={
}
// 實(shí)例化Zhangsan對(duì)象
var zs = new Zhangsan("張三");
console.info(zs.type);    // undefined

我們?cè)赯hangsan的構(gòu)造函數(shù)里調(diào)用Person()函數(shù),希望它內(nèi)部的ths.xxx可以在Zhangsan類(lèi)的構(gòu)造函數(shù)里執(zhí)行一遍,但奇怪的是,出現(xiàn)“console.info(zs.type);”時(shí),輸出的是undefined,這是怎么回事呢?

這和Person的調(diào)用方式有關(guān)。在JavaScript中,function有兩種不同的調(diào)用方法:

  1. 作為函數(shù)存在,直接用“()”調(diào)用,例如“function test(){}; test();”test被用作函數(shù),直接被“()”符號(hào)調(diào)用。

  2. 作為類(lèi)的構(gòu)造函數(shù)存在,使用new調(diào)用,例如“function test(){}; new test();”test作為類(lèi)的構(gòu)造函數(shù),通過(guò)new進(jìn)行test類(lèi)的實(shí)例化。這兩種方法的調(diào)用,function內(nèi)部的this指向會(huì)有所不同---作為函數(shù)的function,其this指向的是window,而作為構(gòu)造函數(shù)的function,其this指向的實(shí)例對(duì)象。

上面代碼中,Zhangsan類(lèi)構(gòu)造函數(shù)中的Person是通過(guò)函數(shù)方式調(diào)用的,它內(nèi)部的this指向的是window對(duì)象,起效果等同于如下代碼:
[代碼4]

// 定義Person類(lèi)
function Person (name){
  this.name = name;
  this.type = "黃";
}
Person.prototype={
  say : function(){
    console.info("我是一個(gè)"+ this.type +"種人,我的名字叫" + this.name);
  }
}  
// 定義Zhangsan類(lèi)
function Zhangsan (name){
  window.name = name;
  window.type = "黃";
}
Zhangsan.prototype={
}
// 實(shí)例化Zhangsan對(duì)象
var zs = new Zhangsan("張三");
console.info(zs.type);  // undefined
console.info(type);    // 黃 (window.type可以省略寫(xiě)成type)

如果想達(dá)到[代碼3]的效果,讓Person內(nèi)部this指向Zhangsan類(lèi)的實(shí)例,可以通過(guò)call或apply方法實(shí)現(xiàn),如下:
[代碼5]

// 定義Person類(lèi)
function Person (name){
  this.name = name;
  this.type = "黃";
}
Person.prototype={
  say : function(){
    console.info("我是一個(gè)"+ this.type +"種人,我的名字叫" + this.name);
  }
}  
// 定義Zhangsan類(lèi)
function Zhangsan (name){
  Person.call(this,name);
}
Zhangsan.prototype={
}
// 實(shí)例化Zhangsan對(duì)象
var zs = new Zhangsan("張三");
console.info(zs.type);    // 黃

構(gòu)造函數(shù)的屬性和行為已經(jīng)成功實(shí)現(xiàn)了繼承,接下來(lái)我們要實(shí)現(xiàn)原型中的屬性和行為的繼承。既然Zhangsan類(lèi)需要和Person類(lèi)原型中同樣的屬性和行為,那么能否將Person類(lèi)的原型直接傳給Zhangsan類(lèi)的原型,如下代碼:
[代碼6]

// 定義Person類(lèi)
function Person (name){
  this.name = name;
  this.type = "黃";
}
Person.prototype={
  say : function(){
    console.info("我是一個(gè)"+ this.type +"種人,我的名字叫" + this.name);
  }
}  
// 定義Zhangsan類(lèi)
function Zhangsan (name){
  Person.call(this,name);
}
Zhangsan.prototype = Person.prototype;
// 實(shí)例化Zhangsan對(duì)象
var zs = new Zhangsan("張三");
// 我是一個(gè)黃種人,我的名字叫張三
zs.say();

通過(guò)Person類(lèi)的原型傳給Zhangsan類(lèi)的原型,Zhangsan類(lèi)成功獲得了say行為,但事情并不像想象中的那么簡(jiǎn)單,如果我們要給Zhangsan類(lèi)添加run行為呢?如下代碼:
[代碼7:添加run行為]

// 定義Person類(lèi)
function Person (name){
  this.name = name;
  this.type = "黃";
}
Person.prototype={
  say : function(){
    console.info("我是一個(gè)"+ this.type +"種人,我的名字叫" + this.name);
  }
}  
// 定義Zhangsan類(lèi)
function Zhangsan (name){
  Person.call(this,name);
}
Zhangsan.prototype = Person.prototype;
Zhangsan.prototype.run = function(){
  console.info("我100米短跑只要10秒!");
}
// 實(shí)例化Zhangsan對(duì)象
var zs = new Zhangsan("張三");
zs.say();  // 我是一個(gè)黃種人,我的名字叫張三
zs.run();  //我100米短跑只要10秒!
var zs2 = new Person("張三2");
zs2.run();  //我100米短跑只要10秒!

我們只想給Zhangsan類(lèi)添加run行為,為什么Person類(lèi)也獲得了run行為了呢?這涉及傳值和傳址的兩個(gè)問(wèn)題----在JavaScript中,賦值語(yǔ)句會(huì)用傳值和傳地址兩種不同的方式進(jìn)行賦值,如果是數(shù)值型、不爾型、字符型等基本數(shù)據(jù)類(lèi)型,在進(jìn)行賦值時(shí)會(huì)將數(shù)據(jù)直接賦值一份,將賦值的那一份數(shù)據(jù)進(jìn)行賦值,也就是通常所說(shuō)的傳值;如果是數(shù)組、hash對(duì)象等復(fù)雜數(shù)據(jù)類(lèi)型,在進(jìn)行賦值時(shí)會(huì)直接用內(nèi)存地址賦值,而不是將數(shù)據(jù)賦值一份,這就是傳址賦值,就是傳數(shù)據(jù)的映射地址。
[代碼8:傳值與傳址]

var a=10;    // 基本數(shù)據(jù)類(lèi)型
var b=a;    // 將變量a保存的值賦值一份,傳給變量b,b和a各保存一份數(shù)據(jù)
var c=[1,2,3];  // 復(fù)雜數(shù)據(jù)類(lèi)型
var d=c;    // 將變量c指向的數(shù)據(jù)內(nèi)存地址傳給變量d,c和d指向同一份數(shù)據(jù)
b++;
d.push(4);
console.info(a);  // 10
console.info(b);  // 11    變量b保存的數(shù)據(jù)更改不會(huì)影響到變量a
console.info(c);  // 1,2,3,4  變量c和d指向同一份數(shù)據(jù),數(shù)據(jù)更改會(huì)相互影響
console.info(d);  // 1,2,3,4

在原生JavaScript中,選擇傳值還是傳地址是根據(jù)數(shù)據(jù)類(lèi)型來(lái)自動(dòng)判斷的,但傳地址有時(shí)候會(huì)給我們帶來(lái)意想不到的麻煩,所以我們需要對(duì)復(fù)雜數(shù)據(jù)類(lèi)型的賦值進(jìn)行控制,讓復(fù)雜數(shù)據(jù)類(lèi)型也可以進(jìn)行傳值。

最簡(jiǎn)單的做法是遍歷數(shù)組或者Hash對(duì)象,將數(shù)組或者Hash對(duì)象這種復(fù)雜的數(shù)據(jù)拆分成一個(gè)個(gè)簡(jiǎn)單數(shù)據(jù),然后分別賦值,如下面代碼:
[代碼9:對(duì)復(fù)雜數(shù)據(jù)類(lèi)型進(jìn)行傳值]

var a = [1, 2, 3] ,b = {name:'張三',sex:'男',tel:'1383838438'};
var c = [] ,d = {};
for(var p in a){
  c[p] = a[p]; 
}
for(var p in b){
  d[p] = b[p];
}
c.push('4');
d.email = 'ibing@outlook.com';
console.info(a);      // [1, 2, 3]
console.info(c);      // [1, 2, 3, "4"]
console.info(b.email);    // undefined
console.info(d.email);    // ibing@outlook.com

值得一提的是,對(duì)于數(shù)組的傳值還可以使用數(shù)組類(lèi)的slice或者concat方法實(shí)現(xiàn),如下面代碼:
[代碼10:數(shù)組傳值的簡(jiǎn)單方法]

var a = [1, 2, 3];
var b = a.slice(), c = a.concat();
b.pop();
c.push(4);
console.info(a);    // [1, 2, 3]
console.info(b);    // [1, 2]
console.info(c);    // [1, 2, 3, 4]

prototype本質(zhì)上也是一個(gè)hash對(duì)象,所以直接用它賦值時(shí)會(huì)進(jìn)行傳址,這也是為什么[代碼7:添加潤(rùn)行為]中,zs2居然會(huì)run的原因。我們可以用for in來(lái)遍歷prototype,從而實(shí)現(xiàn)prototype的傳值。但因?yàn)閜rototype和function(用做類(lèi)的function)的關(guān)系,我們還有另外一種方法實(shí)現(xiàn)prototype的傳值----new SomeFunction(),如下面代碼:
[代碼11]

// 定義Person類(lèi)
function Person (name){
  this.name = name;
  this.type = "黃";
}
Person.prototype={
  say : function(){
    console.info("我是一個(gè)"+ this.type +"種人,我的名字叫" + this.name);
  }
}  
// 定義Zhangsan類(lèi)
function Zhangsan (name){
  Person.call(this,name);
}
Zhangsan.prototype = new Person();
Zhangsan.prototype.constructor = Person;
Zhangsan.prototype.run = function(){
  console.info("我100米短跑只要10秒!");
}
  
// 實(shí)例化Zhangsan對(duì)象
var zs = new Zhangsan("張三");
zs.say();  // 我是一個(gè)黃種人,我的名字叫張三
zs.run();  // 我100米短跑只要10秒!
var zs2 = new Person("張三2");
zs2.run();  // TypeError: zs2.run is not a function

您是否注意到上面這句Zhangsan.prototype.constructor = Person;,這是因?yàn)閆hangsan.prototype = new Person();時(shí),Zhangsan.prototype.constructor指向了Person,我們需要將它糾正,重新指向Zhangsan。

看完上述內(nèi)容,是不是對(duì)JavaScript中繼承的用法有進(jìn)一步的了解,如果還想學(xué)習(xí)更多內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI