在线上有很多关于合成与继承的信息,但是我还没有找到像样的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组成的信息,只有其他语言的信息。
有人可以给我一个使用以上代码的示例来演示组合和聚合吗?
处理组合与继承时,语言无关紧要。如果您了解什么是类以及什么是类的实例,那么您便拥有了所需的一切。
组成只是一个类由其他类组成;换句话说,一个对象的实例引用了其他对象的实例。
继承是一个类从另一个类继承方法和属性的时间。
假设您有两个功能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。
对于继承,您将拥有
// 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
此处的调用将的范围设置someMethodOnA
为this
,这是C的实例。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句