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

資訊專欄INFORMATION COLUMN

JavaScript中的繼承入門

whatsns / 931人閱讀

摘要:如前所述,中繼承是要分別繼承構造函數和原型中的屬性和行為的。作為類的構造函數存在,使用調用,例如作為類的構造函數,通過進行類的實例化。

正統的面相對象的語言都會提供extend之類的方法用于出來類的繼承,但Javascript并不提供extend方法,在Javascript中使用繼承需要用點技巧。

Javascript中的實例的屬性和行為是由構造函數和原型兩部分組成的,我們定義兩個類:Person和zhangsan,它們在內存中的表現如下圖1:

如果想讓Zhangsan繼承Person,那么我們需要把Person構造函數和原型中的屬性和行為全部傳給Zhangsan的構造函數和原型,如下圖2所示:

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

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

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

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

運行正常,但我們怎么沒看到繼承的“味道”呢?我們在Zhangsan的構造函數中將Person的屬性和行為復制了一份,與其說是繼承不如說是“真巧,這兩個類的構造函數除了函數名不同,其他地方都長得一樣”。她的缺點很明顯:如果Person類的構造函數有任何變動,我們也需要手動的同步修改Zhangsan類的構造函數,同樣一份代碼,我們復制了一份寫在了程序中 的不同地方,這違法了DRY原則,降低了代碼的可維護性。

好了,讓我們來改進它:
[代碼3]

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

我們在Zhangsan的構造函數里調用Person()函數,希望它內部的ths.xxx可以在Zhangsan類的構造函數里執行一遍,但奇怪的是,出現“console.info(zs.type);”時,輸出的是undefined,這是怎么回事呢?

這和Person的調用方式有關。在JavaScript中,function有兩種不同的調用方法:

作為函數存在,直接用“()”調用,例如“function test(){}; test();”test被用作函數,直接被“()”符號調用。

作為類的構造函數存在,使用new調用,例如“function test(){}; new test();”test作為類的構造函數,通過new進行test類的實例化。這兩種方法的調用,function內部的this指向會有所不同---作為函數的function,其this指向的是window,而作為構造函數的function,其this指向的實例對象。

上面代碼中,Zhangsan類構造函數中的Person是通過函數方式調用的,它內部的this指向的是window對象,起效果等同于如下代碼:
[代碼4]

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

如果想達到[代碼3]的效果,讓Person內部this指向Zhangsan類的實例,可以通過call或apply方法實現,如下:
[代碼5]

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

構造函數的屬性和行為已經成功實現了繼承,接下來我們要實現原型中的屬性和行為的繼承。既然Zhangsan類需要和Person類原型中同樣的屬性和行為,那么能否將Person類的原型直接傳給Zhangsan類的原型,如下代碼:
[代碼6]

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

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

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

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

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

在原生JavaScript中,選擇傳值還是傳地址是根據數據類型來自動判斷的,但傳地址有時候會給我們帶來意想不到的麻煩,所以我們需要對復雜數據類型的賦值進行控制,讓復雜數據類型也可以進行傳值。

最簡單的做法是遍歷數組或者Hash對象,將數組或者Hash對象這種復雜的數據拆分成一個個簡單數據,然后分別賦值,如下面代碼:
[代碼9:對復雜數據類型進行傳值]

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

值得一提的是,對于數組的傳值還可以使用數組類的slice或者concat方法實現,如下面代碼:
[代碼10:數組傳值的簡單方法]

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本質上也是一個hash對象,所以直接用它賦值時會進行傳址,這也是為什么[代碼7:添加潤行為]中,zs2居然會run的原因。我們可以用for in來遍歷prototype,從而實現prototype的傳值。但因為prototype和function(用做類的function)的關系,我們還有另外一種方法實現prototype的傳值----new SomeFunction(),如下面代碼:
[代碼11]

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

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

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79039.html

相關文章

  • Javascript面向對象從入門到重新入門--關于繼承

    摘要:這個構造函數的不管從調用方式還是內部寫法就都很有的感覺,但是從用途上來說,它其實更靠近的概念是中的工廠方法。到這里,所有關于繼承的東西講完了,接下來準備準備說說當中的封裝 所謂的對象,就是抽象化的數據本身 一個面向對象轉向面向原型的困惑 我發現Javascript這門語言每次翻開都會帶給人新感受,尤其是看完其他語言的面向對象再來看它,但是如果你也是過來人就一定記得教科書里面冗長乏味的...

    3fuyu 評論0 收藏0
  • 帶你入門 JavaScript ES6 (四)

    摘要:方法如示例中定義的方法靜態方法使用關鍵字修飾的方法,允許通過類名直接調用靜態方法而無需實例化。 本文同步帶你入門 JavaScript ES6 (四),轉載請注明出處。 前面我們學習了: for of 變量和擴展語法 塊作用域變量和解構 箭頭函數 本章我們將學習 ES6 中的 類,了解類基本定義和繼承相關知識 一、概述 ES6 中的 類 是基于原型的繼承語法糖,本質上它是一個 fu...

    he_xd 評論0 收藏0
  • 前端文檔收集

    摘要:系列種優化頁面加載速度的方法隨筆分類中個最重要的技術點常用整理網頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數組函數數據訪問性能優化方案實現的大排序算法一怪對象常用方法函數收集數組的操作面向對象和原型繼承中關鍵詞的優雅解釋淺談系列 H5系列 10種優化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個最重要的技術點 常用meta整理 網頁性能管理詳解 HTML5 ...

    jsbintask 評論0 收藏0
  • 前端文檔收集

    摘要:系列種優化頁面加載速度的方法隨筆分類中個最重要的技術點常用整理網頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數組函數數據訪問性能優化方案實現的大排序算法一怪對象常用方法函數收集數組的操作面向對象和原型繼承中關鍵詞的優雅解釋淺談系列 H5系列 10種優化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個最重要的技術點 常用meta整理 網頁性能管理詳解 HTML5 ...

    muddyway 評論0 收藏0

發表評論

0條評論

whatsns

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<