JavaScript中的组合,继承和聚合 [英] Composition, Inheritance, and Aggregation in JavaScript

查看:101
本文介绍了JavaScript中的组合,继承和聚合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在线有很多关于组合与继承的信息,但我还没有找到适合JavaScript的例子。使用下面的代码来演示继承:

  function stock(/ *带有股票名称和价格的对象* /){
for(参数[0]中的var company_name){
//将传递的对象复制到构造函数创建的新对象
this [company_name] = arguments [0] [company_name];
}
}

//原型中的示例方法,它们的实现对于
//这个问题可能是多余的,但是list()返回一个带有toString的数组( ); total()
//将股票价格加起来并返回它们。使用ES5功能使
//继承属性不可枚举

Stock.prototype = {
list:function(){
var company_list = [];
for(var company_name in this)
company_list.push(company_name);
return company_list.toString();
},
总计:function(){
var price_total = 0;
for(var company_name in this)
price_total + = this [company_name];
返回'$'+ 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的人似乎只使用原型和继承呢?我没有在网上找到很多关于组合的信息,只有其他语言的信息。



有人能举例说明使用上面的代码来演示组合和聚合吗?

解决方案

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



组成就是一个类是其他类的编组;或者换句话说,对象的实例引用了其他对象的实例。



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



假设您有两个功能,A和B.您想要定义第三个功能,C,它具有A和B中的部分或全部功能。您可以选择使C从B和A扩展,在这种情况下C具有B和A的所有内容,因为C isA B和A,或者您可以使C的每个实例都有一个实例A和B的一个实例,并调用这些功能上的项目。在后一种情况下,每个实例C实际上包装了B的实例和A的实例。



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



现在,对于特定于语言的详细信息...



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



按照上面的例子,你有A,B和C.



对于继承,你会有

  //定义一个对象(可以看作是类)
函数A(){}

//定义一些功能
A.prototype.someMethod = function(){}

如果你想让C扩展A,你会做

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

现在C的每个实例都有方法 someMethod ,因为C的每个实例都是A。



Javascript没有多重继承*(后面会详细介绍),所以你不能拥有C但是,您可以使用合成来为其提供功能。实际上,这是一些原因使得某些人更倾向于继承遗产;组合功能没有限制(但这不是唯一的原因)。

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

// C上的someMethod调用B上的someMethod。
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(){
thi s.a.someMethod(arg1,arg2 ...);
}

这里你没有搞乱范围,你只是传递参数。这是一种简单且非常可行的方法。 (参数可以来自这个或传入,无论如何......)



另一种方法将使用javascript的调用(或 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中,函数是对象,因此您可以将它们分配给变量。 / p>

这里的调用调用是设置 someMethodOnA 的范围到这个,这是C的实例。


There is a lot of information about composition vs inheritance online, but I haven't found decent examples with JavaScript. Using the below code to demonstrate inheritance:

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

(To make the code smaller, you can leave out the method implementations, like: Stock.total = function(){ /* code */ } I just put them in there to be fancy). If composition is favored for a lot of situations in OOP, how come most people using JavaScript seem to only use prototypes and inheritance? I did not find a lot of information about composition in JavaScript online, only in other languages.

Can someone give me an example using the above code to demonstrate composition and aggregation?

解决方案

The language is irrelevant when dealing with composition vs inheritance. If you understand what class is and what an instance of a class is, then you have all you need.

Composition is simply when a class is composed of other classes; or to say it another way, an instance of an object has references to instances of other objects.

Inheritance is when a class inherits methods and properties from another class.

Let's say you have two functionality, A and B. You want to define a third functionality, C, that has some or all of both A and B. You could either make C extend from B and A, in which case C has everything B and A has because C isA B and A, or you can make each instance of C have an instance of A and an instance of B, and invoke items on those functionalities. In the latter case, each instance C in effect wraps an instance of B and an instance of A.

Of course, depending on the language, you might not be able to have a class extend from 2 classes (e.g. Java doesn't support multiple inheritance), but that's a language specific detail that has nothing to do with the concept.

Now, for the language specific details...

I used the word class, but javascript has no notion of Class as such. It has objects, and thats it (other than the simple types). Javascript uses prototypal inheritance, which means it has a way of efficiently defining objects and the methods on those objects (this is the topic for another question; you can search SO as there are already answers.)

So going with our example above, you have A, B, and C.

For inheritance, you would have

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

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

If you wanted C to extend A, you would do

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

Now every instance of C would have the method someMethod, because every instance of C "isA" A.

Javascript doesn't have multiple inheritance* (more on this later), so you can't have C extend both A and B. You can use composition, however, to give it the functionality. Indeed, this is one of the reasons composition is preferred by some over inheritance; there are no limits on combining functionality (but this isn't the only reason).

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

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

So there are your simple examples for both inheritance and composition. However, this is not the end of the story. I said before that Javascript does not support multiple inheritance, and in a sense it doesn't, because you can't base the prototype of an object off the prototypes of multiple objects; i.e. you can't do

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

because as soon as you do the third, line, you just undid the the second line. This has implications for the instanceof operator.

However, this doesn't really matter, because just because you can't redefine the constructor of an object twice, you can still add any methods you want to the prototype of an object. So just because you can't do the above example, you can still add anything you want to C.prototype, including all the methods on the prototypes of both A and B.

Many frameworks support this and make it easy. I do a lot of Sproutcore work; with that framework you can do

A = {
   method1: function(){}
}

B = {
   method2: function(){}
}

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

Here I defined functionality in object literals A and B, and then added the functionality of both to C, so every instance of C has methods 1, 2, and 3. In this particular case, the extend method (provided by the framework) does all the heavy lifting of setting up the prototypes of the objects.

EDIT -- In your comments, you bring out a good question, namely "If you use composition, how do you reconcile the scope of the main object against the scope of the objects of which the main object is composed".

There are a bunch of ways. The first is simply to pass arguments. So

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

Here you are not messing with scopes, you are simply passing arguments around. This is a simple and very viable approach. (the arguments can come from this or be passed in, whatever...)

Another way to do it would be to use the call (or apply) methods of javascript, which basically allows you to set the scope of a function.

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

to be a bit more clear, the following is equivalent

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

In javascript, functions are object, so you can assign them to variables.

the call invocation here is setting the scope of someMethodOnA to this, which is the instance of C.

这篇关于JavaScript中的组合,继承和聚合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆