小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

JavaScript中的類繼承

 duduwolf 2005-11-23

JavaScript中的類繼承

Douglas Crockford
www.

翻譯 ShiningRay

www.

 

And you think you‘re so clever and classless and free

  • John Lennon

JavaScript 一種沒有類的,面向?qū)ο蟮恼Z言,它使用原型繼承來代替類繼承。這個可能對受過傳統(tǒng)的面向?qū)ο笳Z言(如C++Java)訓(xùn)練的程序員來說有點迷惑。JavaScript的原型繼承比類繼承有更強大的表現(xiàn)力,現(xiàn)在就讓我們來看看。i

Java

JavaScript

Strongly-typed

Loosely-typed

Static

Dynamic

Classical

Prototypal

Classes

Functions

Constructors

Functions

Methods

Functions

但首先,為什么我們?nèi)绱岁P(guān)心繼承呢?主要有兩個原因。第一個是類型有利。我們希望語言系統(tǒng)可以自動進行類似類型引用的轉(zhuǎn)換cast。小類型安全可以從一個要求程序顯示地轉(zhuǎn)換對象引用的類型系統(tǒng)中獲得。這是強類型語言最關(guān)鍵的要點,但是這對像JavaScript這樣的弱類型語言是無關(guān)的,JavaScript中的類引用無須強制轉(zhuǎn)換。

第二個原因是為了代碼的服用。在程序中常常會發(fā)現(xiàn)很多對象都會實現(xiàn)同一些方法。類讓建立單一的一個定義集中建立對象成為可能。在對象中包含其他對象也包含的對象也是很常見的,但是區(qū)別僅僅是一小部分方法的添加或者修改。類繼承對這個十分有用,但原型繼承甚至更有用。

要展示這一點,我們要介紹一個小小的“甜點”可以主我們像一個常規(guī)的類語言一樣寫代碼。我們?nèi)缓髸故疽恍┰陬愓Z言中沒有的有用的模式。最后,我們會就會解釋這些“甜點”。

類繼承

首先,我們建立一個Parenizor 類,它有成員 value getset方法,還有一個會將value包裝在括號內(nèi)的toString方法。

function Parenizor(value) {
    this.setValue(value);
}

Parenizor.method(‘setValue‘, function (value) {
    this.value = value;
    return this;
});

Parenizor.method(‘getValue‘, function () {
    return this.value;
});

Parenizor.method(‘toString‘, function () {
    return ‘(‘ + this.getValue() + ‘)‘;
});

這個語法可能沒什么用,但它很容易看出其中類的形式。method 方法接受一個方法名和一個函數(shù),并把它們放入類中作為公共方法。T

現(xiàn)在我們可以寫成

myParenizor = new Parenizor(0);
myString = myParenizor.toString();

正如期望的那樣,myString "(0)"

現(xiàn)在我們要建立另一個繼承自Parenizor 類,它基本上是一樣的除了 toString 方法將會產(chǎn)生"-0-" 如果 value 是零或者空。

function ZParenizor(value) {
    this.setValue(value);
}

ZParenizor.inherits(Parenizor);

ZParenizor.method(‘toString‘, function () {
    if (this.getValue()) {
        return this.uber(‘toString‘);
    }
    return "-0-";
});

The inherits 方法類似于Javaextends 。uber 方法類似于Javasuper 。它令一個方法調(diào)用父類的方法(更改了名稱是為了避免和保留字沖突).

我們可以寫成這樣

myZParenizor = new ZParenizor(0);
myString = myZParenizor.toString();

這次, myString "-0-".

JavaScript 并沒有類,但我們可以編程達到這個目的。

多繼承

通過操作一個函數(shù)的prototype 對象,我們可以實現(xiàn)多繼承?;旌隙嗬^承難以實現(xiàn)而且可能會遭到名稱沖突的危險。我們可以在JavaScript中實現(xiàn)混合多繼承,但這個例子我們將使用一個較規(guī)范的形式稱為瑞士繼承 Swiss Inheritance.

假設(shè)有一個NumberValue 類有一個setValue 方法用來檢查 value 是不是在一個指定范圍內(nèi)的一個數(shù),并在適當(dāng)?shù)臅r候拋出異常。我們只要它的 setValue setRange 方法給我們的 ZParenizor 。我們當(dāng)然不想要它的 toString 方法。這樣,我們寫到:

ZParenizor.swiss(NumberValue, ‘setValue‘, ‘setRange‘);

這個將僅僅添加需要的方法。

寄生繼承

這是另一個書寫 ZParenizor 類的方法。并不從 Parenizor 繼承,而是寫了一個調(diào)用了Parenizor 構(gòu)造器的構(gòu)造器,并對結(jié)果修改最后返回這個結(jié)果。這個構(gòu)造器添加的是特權(quán)方法而非公共方法。

function ZParenizor2(value) {
    var self = new Parenizor(value);
    self.toString = function () {
        if (this.getValue()) {
            return this.uber(‘toString‘);
        }
        return "-0-"
    };
    return self;
}

類繼承是一種“是……”的關(guān)系,而寄生繼承是一個關(guān)于“原是……而現(xiàn)在是……”的關(guān)系。構(gòu)造器在對象的構(gòu)造中扮演了大量的角色。注意 uber (代替 super 關(guān)鍵字)對特權(quán)方法仍有效。

類擴展

JavaScript的動態(tài)性讓我們可以對一個已有的類添加或替換方法。我們可以在任何時候調(diào)用方法。我們可以隨時地擴展一個類。繼承不是這個方式。所以我們把這種情況稱為“類擴展”來避免和Javaextends ──也叫擴展,但不是一回事──相混淆。

對象擴展

在靜態(tài)面向?qū)ο笳Z言中,如果你想要一個對象和另一個對象有所區(qū)別,你必須新建立一個類。但在JavaScript中,你可以向單獨的對象添加方法而不用新建類。這會有巨大的能量因為你就可以書寫盡量少的類,類也可以寫得更簡單。想想JavaScript的對象就像哈希表一樣。你可以在任何時候添加新的值。如果這個值是一個函數(shù),那他就會成為一個方法。

這樣在上面的例子中,我完全不需要 ZParenizor 類。我只要簡單修改一下我的實例就行了。

myParenizor = new Parenizor(0);
myParenizor.toString = function () {
    if (this.getValue()) {
        return this.uber(‘toString‘);
    }
    return "-0-";
};
myString = myParenizor.toString();

我們給 myParenizor 實例添加了一個 toString 方法而沒有使用任何繼承。我們可以演化單獨的實例因為這個語言是無類型的。

小甜點

要讓上面的例子運行起來,我寫了四個“甜點”方法。首先,method 方法,可以把一個實例方法添加到一個類中。

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

這個將會添加一個公共方法到 Function.prototype 中,這樣通過類擴展所有的函數(shù)都可以用它了。它要一個名稱和一個函數(shù)作為參數(shù)。

它返回 this 。當(dāng)我寫一個沒有返回值的方法時,我通常都會讓它返回 this 。這樣可以形成鏈?zhǔn)秸Z句。

下面是 inherits 方法,它會指出一個類是繼承自另一個類的。它必須在兩個類都定義完了之后才能定義,但要在方法繼承之前調(diào)用。

Function.method(‘inherits‘, function (parent) {
    var d = 0, p = (this.prototype = new parent());

    this.method(‘uber‘, function uber(name) {
        var f, r, t = d, v = parent.prototype;
        if (t) {
            while (t) {
                v = v.constructor.prototype;
                t -= 1;
            }
            f = v[name];
        } else {
            f = p[name];
            if (f == this[name]) {
                f = v[name];
            }
        }
        d += 1;
        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
        d -= 1;
        return r;
    });
    return this;
});

再來,我們擴展 Function 類。我們加入一個 parent 類的實例并將它做為新的 prototype 。我們也必須修正 constructor 字段,同時我們加入 uber 方法。

uber 方法將會在自己的prototype 查找某個方法。這個是寄生繼承或類擴展的一種情況。如果我們是類繼承,那么我們要找到 parentprototype 中的函數(shù)。return 語句調(diào)用了函數(shù)的apply方法來調(diào)用該函數(shù),同時顯示地設(shè)置 this并傳遞參數(shù)。參數(shù)(如果有的話)可以從arguments 數(shù)組中獲得。不幸的是, arguments 數(shù)組并不是一個真正的數(shù)組,所以我們又要用到 apply 來調(diào)用數(shù)組中的 slice 方法。

最后,swiss 方法

Function.method(‘swiss‘, function (parent) {
    for (var i = 1; i < arguments.length; i += 1) {
        var name = arguments[i];
        this.prototype[name] = parent.prototype[name];
    }
    return this;
});

The swiss 方法對每個參數(shù)進行循環(huán)。每個名稱,它都將 parent的原型中的成員復(fù)制下來到新的類的 prototype 中。

總結(jié)

JavaScript可以像類語言那樣使用,但它也有一種十分獨特的表現(xiàn)層次。我們已經(jīng)看過了類繼承、瑞士繼承、寄生繼承、類擴展和對象擴展。這一等系列代碼復(fù)用的模式都能來自這個一直被認(rèn)為是很小、很簡單的JavaScript語言。

類對象屬于“硬的”。給一個“硬的”對象添加成員的唯一的方法是建立一個新的類。在JavaScript中,對象是“軟的”。要給一個“軟”對象添加成員只要簡單的賦值就行了。

因為JavaScript中的類是這樣地靈活,你可能會還想到更復(fù)雜的類繼承。但深度繼承并不合適。淺繼承則較有效而且更易表達。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多