Javascript继承和封装,高效完成

时间:2015-02-17 09:23:20

标签: javascript inheritance encapsulation

来自C ++ / Objective-C背景,我试图学习如何正确有效地再现Javascript中的继承和封装模式。我已经做了大量的阅读(Crockford等),虽然有很多例子可以说明如何实现其中一个,但我在努力解决如何在不引入重大负面影响的情况下将它们结合起来。

目前,我有这段代码:

var BaseClass = (function() {

    function doThing() {
        console.log("[%s] Base-class's 'doThing'", this.name);
    }

    function reportThing() {
        console.log("[%s] Base-class's 'reportThing'", this.name);
    }

    return function(name) {
        var self = Object.create({});

        self.name = name;

        self.doThing = doThing;
        self.reportThing = reportThing;

        return self;
    }

}());

var SubClass = (function(base) {

    function extraThing() {
        console.log("[%s] Sub-class's 'extraThing'", this.name);
    }

    function doThing() {
        console.log("[%s] Sub-class's replacement 'doThing'", this.name);
    }    

    return function(name) {
        // Create an instance of the base object, passing our 'name' to it.
        var self = Object.create(base(name));    


        // We need to bind the new method to replace the old
        self.doThing = doThing;
        self.extraThing = extraThing;

        return self;
    }

}(BaseClass));

主要是做我想要的:

// Create an instance of the base class and call it's two methods
var base = BaseClass("Bert");

base.doThing();         // "[Bert] Base-class's 'doThing'"
base.reportThing();     // "[Bert] Base-class's 'reportThing'"

var other = BaseClass("Fred");


// Create an instance of the sub-class and call it's three methods (two from the base, one of it's own)
var sub = SubClass("Alfred");

sub.doThing();          // "[Alfred] Sub-class's replacement 'doThing'"
sub.extraThing();       // "[Alfred] Sub-class's 'extraThing'"
sub.reportThing();      // "[Alfred] Base-class's 'reportThing'"

但是,至少有两个问题:

  • 我不相信原型链完好无损。如果我通过子类的一个实例替换原型中的方法,其他实例看不到它:
  • 没有封装.name属性

我正在替换像这样的函数的原型实现:

Object.getPrototypeOf(oneInstance).reportThing = function() { ... }
otherInstance.reportThing()    // Original version is still called

这可能不是一个重大问题,但它让我怀疑我的理解。

私有变量是我想要有效实现的东西。变量隐藏的模块模式在这里没有帮助,因为它导致函数定义存在于每个对象。我可能错过了一种组合模式的方法,那么有没有一种方法可以在不重复功能的情况下实现私有变量?

3 个答案:

答案 0 :(得分:1)

简单的配方如下:

function BaseClass(someParams)
{
   // Setup the public properties, e.g.
   this.name = someParams.name;
}

BaseClass.prototype.someMethod = function(){
   // Do something with the public properties
}

现在继承以这种方式发生

function SubClass(someParams)
{ 
    // Reuse the base class constructor
    BaseClass.call(this, someParams);

    // Keep initializing stuff that wasn't initialized by the base class
    this.anotherProperty= someParams.anotherProperty;
}

// Copy the prototype from the BaseClass
SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;

// Start extending or overriding stuff
SubClass.prototype.someMethod = function(){

   // In case you still wanna have the side effects of the original method
   // This is opt-in code so it depends on your scenario.
   BaseClass.prototype.someMethod.apply(this, arguments);

   // Override the method here       
}

取自: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

P.S。所有旧浏览器可能都不支持Object.create,但不要担心,此链接中有一个polyfill。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

答案 1 :(得分:1)

这通常是我在JavaScript中处理继承和封装的方式。 defclass函数用于创建一个不从任何其他类继承的新类,extend函数用于创建一个扩展另一个类的新类:

var base = new BaseClass("Bert");

base.doThing();     // "Bert BaseClass doThing"
base.reportThing(); // "Bert BaseClass reportThing"

var sub = new SubClass("Alfred");

sub.doThing();     // "Alfred SubClass replacement doThing"
sub.extraThing();  // "Alfred SubClass extraThing"
sub.reportThing(); // "Alfred BaseClass reportThing"

var other = new SubClass("Fred");

SubClass.prototype.reportThing = function () {
    console.log(this.name + " SubClass replacement reportThing");
};

other.reportThing(); // Fred SubClass replacement reportThing
<script>
function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

function extend(constructor, keys) {
    var prototype = Object.create(constructor.prototype);
    for (var key in keys) prototype[key] = keys[key];
    return defclass(prototype);
}

var BaseClass = defclass({
    constructor: function (name) {
        this.name = name;
    },
    doThing: function () {
        console.log(this.name + " BaseClass doThing");
    },
    reportThing: function () {
        console.log(this.name + " BaseClass reportThing");
    }
});

var SubClass = extend(BaseClass, {
    constructor: function (name) {
        BaseClass.call(this, name);
    },
    doThing: function () {
        console.log(this.name + " SubClass replacement doThing");
    },
    extraThing: function () {
        console.log(this.name + " SubClass extraThing");
    }
});
</script>

阅读以下答案,了解继承如何在JavaScript中运行:

What are the downsides of defining functions on prototype this way?

它解释了原型和构造函数之间的区别。此外,它还展示了原型和类是如何同构的以及如何在JavaScript中创建“类”。

希望有所帮助。

答案 2 :(得分:0)

如果要保留原型链,则必须覆盖并使用.prototype: 例: 主类:

function BaseClass(){

}
BaseClass.prototype.doThing = function(){...}

亚类:

function SubClass(){
}
SubClass.prototype= new BaseClass();
SubClass.prototype.extraThing = function(){};

现在,无论何时更改extraThing或doThing,它都会在任何地方被替换。 name属性可以作为公共变量访问(它不是静态的)。

如果你想要它是静态的,你必须把它放在原型中。

如果你想把它变成私人的,你就可以使它在本地运作:

function BaseClass(nameParam){
 var name = nameParam;
}

要创建对象,只需调用函数:

var testObj = new BaseClass("test");
testObj.doThing();

如果要将私有变量与可重写函数组合在一起,您可能会找到answer here。但是如果你能够重写有权访问私有变量的函数,它就不再是私有变量了。