Javascript继承封装,高效完成

Javascript inheritance and encapsulation, done efficiently

来自 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'"

但是,有(至少!)两个问题:

我正在替换原型的函数实现,如下所示:

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

这也许不是什么大问题,但它让我怀疑我的理解。

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

如果你想保留原型链,你必须覆盖并使用.prototype: 例子: 主要 Class:

function BaseClass(){

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

子Class:

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

现在,只要您更改 extraThing 或 doThing,它就会被替换到任何地方。 名称 属性 可以作为 public 变量访问(它不是静态的)。

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

如果你想让它私有,你必须让它在本地运行:

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

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

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

如果你想将私有变量与可重写函数结合起来,你可能会找到你的 answer here。但是如果你能够重写访问私有变量的函数,它就不再是真正的私有变量了。

简单的食谱如下:

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 可能并非所有旧浏览器都支持,但别担心,这个 link 中有一个 polyfill。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

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

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?

它解释了原型和构造函数之间的区别。此外,它还展示了原型和 classes 是如何同构的,以及如何在 JavaScript.

中创建“classes”

希望对您有所帮助。