簡體   English   中英

克羅克福德的原型繼承 - 用法

[英]Crockford's Prototypical Inheritance - Usage

我一直在構建一個小型JS框架供我工作使用,我想使用Douglas Crockford的原型繼承模式。 我想我對原型對象的工作原理有了一般的了解,但不清楚的是我將使用這種模式的方式超出了最簡單的例子。

我會把它充實到我明白的地步。

(function () {

    'use strict';

    var Vehicles = {};

    Vehicles.Vehicle = function () {
        this.go = function () {
            //go forwards
        };

        this.stop = function () {
            //stop
        };
    };

    Vehicles.Airplane = Object.create(Vehicles.Vehicle());

}());

所以現在我的Vehicles.Airplane對象可以去()和停止(),但我想要更多。 我想將takeOff()和land()方法添加到此對象。 之后我可以使用丑陋的點符號:

Vehicles.Airplane.takeOff = function () {
    //take off stuff
}

但這似乎是錯誤的,特別是如果我要添加許多方法或屬性。 在這里提出問題似乎與我的非常相似,但答案對我來說並不完全正確。 答案表明我應該在使用Object.create之前構建一個對象文字,並且我應該將該對象文字傳遞給create方法。 但是,在給出的示例代碼中,看起來它們的新對象現在根本沒有繼承。

我希望的是一些語法類似於:

Vehicles.Airplane = Object.create(Vehicles.Vehicle({
    this.takeOff = function () {
        //takeOff stuff
    };
    this.land = function () {
        //land stuff
    };
}));

我知道這個語法現在會用Object.create打破,因為我當然正在傳遞Vehicle.Vehicle一個函數而不是一個對象文字。 這不是重點。 我想知道我應該以什么方式將一個新屬性構建到一個繼承自另一個的對象中,而不必在事后用點符號一次列出一個。


編輯:

Bergi在經歷了一些關於這個話題的痛苦思考后,我想我真的想要用你所描述的“古典模式”。 這是我的第一次嘗試(現在使用實際的代碼片段,而不是模擬假設 - 你甚至可以看到我糟糕的方法存根):

CS.Button = function (o) {
    o = o || {};

    function init(self) {
        self.domNode = dce('a');
        self.text = o.text || '';
        self.displayType = 'inline-block';
        self.disabled = o.disabled || false;

        self.domNode.appendChild(ctn(self.text));
        if (o.handler) {
            self.addListener('click', function () {
                o.handler(self);
            });
        }
    }

    this.setText = function (newText) {
        if (this.domNode.firstChild) {
            this.domNode.removeChild(this.domNode.firstChild);
        }
        this.domNode.appendChild(ctn(newText));
    };

    init(this);
};
CS.Button.prototype = Object.create(CS.Displayable.prototype, {
    constructor: {value: CS.Button, configurable: true}
});

CS.Displayable = function (o) { // o = CS Object
    o = o || {};

    var f = Object.create(new CS.Element(o));

    function init(self) {
        if (!self.domAnchor) {
            self.domAnchor = self.domNode;
        }
        if (self.renderTo) {
            self.renderTo.appendChild(self.domAnchor);
        }
    }

    //Public Methods
    this.addClass = function (newClass) {
        if (typeof newClass === 'string') {
            this.domNode.className += ' ' + newClass;
        }
    };
    this.addListener = function (event, func, capture) {
        if (this.domNode.addEventListener) {
            this.domNode.addEventListener(event, func, capture);
        } else if (this.domNode.attachEvent) {
            this.domNode.attachEvent('on' + event, func);
        }
    };
    this.blur = function () {
        this.domNode.blur();
    };

    this.disable = function () {
        this.disabled = true;
    };

    this.enable = function () {
        this.disabled = false;
    };

    this.focus = function () {
        this.domNode.focus();
    };

    this.getHeight = function () {
        return this.domNode.offsetHeight;
    };

    this.getWidth = function () {
        return this.domNode.offsetWidth;
    };

    this.hide = function () {
        this.domNode.style.display = 'none';
    };

    this.isDisabled = function () {
        return this.disabled;
    };

    this.removeClass = function (classToRemove) {
        var classArray = this.domNode.className.split(' ');
        classArray.splice(classArray.indexOf(classToRemove), 1);
        this.domNode.className = classArray.join(' ');
    };

    this.removeListener = function () {
        //Remove DOM element listener
    };

    this.show = function () {
        this.domNode.style.display = this.displayType;
    };

    init(this);
};
CS.Displayable.prototype = Object.create(CS.Element.prototype, {
    constructor: {value: CS.Displayable, configurable: true}
});

我應該很清楚地說它還沒有完全奏效,但大多數情況下我都希望你對我是否在正確的軌道上有所了解。 您在示例中的注釋中提到了“特定於實例的屬性和方法”。 這是否意味着我的this.setText方法和其他方法被錯誤地放置,並且原型鏈上的后代項目將無法使用?

此外,當使用時,似乎聲明的順序現在很重要(我無法訪問CS.Displayable.prototype,因為(我認為)首先列出CS.Button,並且在我'時,CS.Displayable未定義我試圖引用它)。 這是我必須要做的事情(在代碼而不是我的OCD字母順序中按照祖先的順序排列)或者是否有我在那里俯瞰的東西?

Vehicles.Airplane = Object.create(Vehicles.Vehicle());

那條線是錯的。 你似乎想要使用new Vehicles.Vehicle - 永遠不要在沒有new情況下調用構造函數!

不過,我不確定你想要使用哪種模式。 我想到了兩個:

古典圖案

您正在使用構造函數,就像在標准JS中一樣。 通過繼承彼此的原型對象並在子實例上應用父構造函數來完成繼承。 您的代碼應如下所示:

Vehicles.Vehicle = function () {
    // instance-specific properties and methods,
    // initialising
}
Vehicles.Vehicle.prototype.go = function () {
     //go forwards
};
Vehicles.Vehicle.prototype.stop = function () {
    //stop
};

Vehicles.Airplane = function() {
    // Vehicles.Vehicle.apply(this, arguments);
    // not needed here as "Vehicle" is empty

    // maybe airplane-spefic instance initialisation
}
Vehicles.Airplane.prototype = Object.create(Vehicles.Vehicle.prototype, {
    constructor: {value:Vehicles.Airplane, configurable:true}
}); // inheriting from Vehicle prototype, and overwriting constructor property

Vehicles.Airplane.prototype.takeOff = function () {
   //take off stuff
};

// usage:
var airplane = new Vehicles.Airplace(params);

純粹的原型模式

您正在使用普通對象而不是構造函數 - 沒有初始化。 要創建實例和設置繼承,只使用Object.create 它就像只有原型對象和空構造函數。 instancof在這里不起作用。 代碼如下所示:

Vehicles.Vehicle = {
    go: function () {
         //go forwards
    },
    stop: function () {
         //stop
    }
}; // just an object literal

Vehicles.Airplane = Object.create(Vehicles.Vehicle); // a new object inheriting the go & stop methods
Vehicles.Airplane.takeOff = function () {
   //take off stuff
};

// usage:
var airplane = Object.create(Vehicles.Airplane);
airplane.prop = params; // maybe also an "init" function, but that seems weird to me

你的Object.create錯了。 第一個參數應該是一個對象 (也許這就是人們建議你傳遞文字的原因)。

在您的第一個示例中,您實際上是在傳遞undefined

Vehicles.Airplane = Object.create(Vehicles.Vehicle()); // the function call will
                                                       // return undefined

以下是可行的,但它不是非常Crockford-ish:

Vehicles.Airplane = Object.create(new Vehicles.Vehicle());

我相信克羅克福德會這樣做的方式(或者,至少,不會抱怨):

var Vehicles = {};

Vehicles.Vehicle = {
    go : function() {
        // go stuff
    },
    stop : function() {
        // go stuff
    }
};

Vehicles.Airplane = Object.create(Vehicles.Vehicle, {
    takeOff : { 
        value : function() {
            // take-off stuff
        }
    },
    land : {
        value: function() {
            // land stuff
        }
    }
});

請注意, Vehicles.Vehicle只是一個文字,它將用作其他對象的原型。 當我們調用Object.create ,我們將Vehicles.Vehicle作為原型傳遞, takeOffland將成為Vehicles.Airplane的屬性。 然后,您可以再次調用Object.create ,如果您想創建例如波音,則將Vehicles.Airplane作為原型。

作為第二個參數傳遞的自己的屬性打包在一個包含其屬性描述符表示的對象中。 外鍵是屬性/方法的名稱,每個鍵都指向包含實際實現的另一個對象作為value 您還可以包含其他鍵,如enumerable ; 如果你不這樣做,他們將采用默認值。 您可以在MDN頁面上閱讀有關Object.defineProperty更多有關描述符的信息

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM