JavaScript中的组合,继承和聚合

布莱恩:

在线上有很多关于合成与继承的信息,但是我还没有找到像样的JavaScript示例。使用以下代码演示继承:

function Stock( /* object with stock names and prices */ ) {
    for (var company_name in arguments[0]) {
        // copy the passed object into the new object created by the constructor
        this[company_name] = arguments[0][company_name]; 
    }
}

// example methods in prototype, their implementation is probably redundant for
// this question, but list() returns an array with toString() invoked; total()
// adds up the stock prices and returns them. Using ES5 feature to make
// inherited properties non-enumerable 

Stock.prototype =  {
    list: function () {
        var company_list = [];
        for (var company_name in this)
            company_list.push(company_name);
        return company_list.toString();
    },
    total: function () {
        var price_total = 0;
        for (var company_name in this)
            price_total += this[company_name];
        return '$' + price_total;
    }
};

Object.defineProperties(Stock.prototype, {
    list: { enumerable: false },
    total: { enumerable:false }
});

var portfolio = new Stock({ MSFT: 25.96, YHOO: 16.13, AMZN: 173.10 });
portfolio.list();  // MSFT,YHOO,AMZN
portfolio.total(); // $215.19

(为使代码更小,您可以省略方法的实现,例如:Stock.total = function(){ /* code */ }我只是将它们放在其中才看中)。如果在OOP中的很多情况下都倾向于使用组合,那么大多数使用JavaScript的人为什么似乎只使用原型和继承?我没有在网上找到很多关于JavaScript组成的信息,只有其他语言的信息。

有人可以给我一个使用以上代码的示例来演示组合和聚合吗?

hvgotcodes:

处理组合与继承时,语言无关紧要。如果您了解什么是类以及什么是类的实例,那么您便拥有了所需的一切。

组成只是一个类其他类组成换句话说,一个对象的实例引用了其他对象的实例。

继承是一个类从另一个类继承方法和属性的时间。

假设您有两个功能A和B。要定义第三个功能C,它同时包含A和B的一部分或全部。您可以使C从B和A扩展,在这种情况下C拥有所有B和A之所以如此,是因为C isAB和A,或者您可以使C的每个实例都具有A的实例和B的实例,并在这些功能上调用项目。在后一种情况下,每个实例C实际上都会包装一个B实例和一个A实例。

当然,根据语言的不同,您可能无法从2个类中扩展一个类(例如Java不支持多重继承),但这是特定于语言的细节,与该概念无关。

现在,有关语言的详细信息...

我使用了单词class,但是javascript没有这样的Class概念。它有对象,就是这样(简单类型除外)。Javascript使用原型继承,这意味着它具有一种有效定义对象和这些对象上的方法的方式(这是另一个问题的主题;您可以在已有答案的情况下搜索SO。)

因此,以上面的示例为例,您拥有A,B和C。

对于继承,您将拥有

// define an object (which can be viewed as a "class")
function A(){}

// define some functionality
A.prototype.someMethod = function(){}

如果您想让C扩展A,则可以

C.prototype = new A();
C.prototype.constructor = A;

现在,C的每个实例都具有方法someMethod,因为C的每个实例“ isA” A。

Javascript没有多重继承*(稍后会详细介绍),因此您不能让C扩展A和B。但是,您可以使用composition来赋予它功能。确实,这是某些人偏爱组合而不是继承的原因之一。组合功能没有限制(但这不是唯一原因)。

function C(){
   this.a = new A();
   this.b = new B();
}

// someMethod on C invokes the someMethod on B.
C.someMethod = function(){
    this.a.someMethod()
}

因此,有一些关于继承和组合的简单示例。但是,这还不是故事的结局。我之前说过Javascript不支持多重继承,从某种意义上说,它不支持,因为您不能基于多个对象的原型建立对象的原型。即你做不到

C.prototype = new B();
C.prototype.constructor = B;
C.prototype.constructor = A;

因为一旦您完成第三行,您就只需取消第二行。这对instanceof操作员有影响

但是,这并不重要,因为仅仅因为您不能两次重新定义对象的构造函数,您仍然可以将所需的任何方法添加到对象的原型中因此,仅因为您不能执行上述示例,您仍然可以向C.prototype添加所需的任何内容,包括A和B原型上的所有方法。

许多框架都支持这一点,并使其变得容易。我做了很多Sproutcore的工作;有了这个框架,你可以做

A = {
   method1: function(){}
}

B = {
   method2: function(){}
}

C = SC.Object.extend(A, B, {
   method3: function(){}
}

在这里,我在对象常量A和中定义了功能B,然后将两者都添加到中C,因此C的每个实例都具有方法1、2和3。在这种特殊情况下,该extend方法(由框架提供)完成了所有繁重的工作设置对象原型的过程。

编辑-在您的评论中,您会提出一个很好的问题,即“如果使用合成,您如何将主要对象的范围与组成主要对象的对象的范围进行调和”。

有很多方法。首先是简单地传递参数。所以

C.someMethod = function(){
    this.a.someMethod(arg1, arg2...);
}

在这里,您不会弄乱范围,只是在传递参数。这是一种简单且非常可行的方法。(参数可以来自this或传递,无论如何...)

做到这一点的另一种方法是使用javascript call(或apply)方法,该方法基本上允许您设置函数的范围。

C.someMethod = function(){
    this.a.someMethod.call(this, arg1, arg2...);
}

更清楚一点,以下等效

C.someMethod = function(){
    var someMethodOnA = this.a.someMethod;
    someMethodOnA.call(this, arg1, arg2...);
}

在javascript中,函数是对象,因此您可以将它们分配给变量。

call此处调用将的范围设置someMethodOnAthis,这是C的实例。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章