Bri*_*ian 57 javascript oop inheritance composition
关于组合与继承在线有很多信息,但我还没有找到适合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
Run Code Online (Sandbox Code Playgroud)
(为了使代码更小,你可以省略方法实现,比如:Stock.total = function(){ /* code */ }
我只是把它们放在那里是为了花哨).如果组合在OOP的很多情况下都受到青睐,为什么大多数使用JavaScript的人似乎只使用原型和继承呢?我没有在网上找到很多关于组合的信息,只有其他语言.
有人可以使用上面的代码给我一个例子来演示组合和聚合吗?
hvg*_*des 75
处理组合与继承时,语言无关紧要.如果您了解哪个类是什么类以及类的实例是什么,那么您就拥有了所需的一切.
当一个类由其他类组成时,组合就是简单的; 或者换句话说,对象的实例引用了其他对象的实例.
继承是指类从其他类继承方法和属性的时间.
假设您有两个功能,A和B.您想要定义第三个功能,C,它具有A和B中的部分或全部.您可以从B和A进行C扩展,在这种情况下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(){}
Run Code Online (Sandbox Code Playgroud)
如果你想让C扩展A,你会这样做
C.prototype = new A();
C.prototype.constructor = A;
Run Code Online (Sandbox Code Playgroud)
现在C的每个实例都有该方法someMethod
,因为C"的所有实例都是"A".
Javascript没有多重继承*(稍后将详细介绍),因此您无法使用C扩展A和B.但是,您可以使用合成来为其提供功能.实际上,这是一些原因,其中一些原因是某些优先于继承; 组合功能没有限制(但这不是唯一的原因).
function C(){
this.a = new A();
this.b = new B();
}
// someMethod on C invokes the someMethod on B.
C.someMethod = function(){
this.a.someMethod()
}
Run Code Online (Sandbox Code Playgroud)
所以有继承和组合的简单示例.然而,这不是故事的结局.我之前说过,Javascript不支持多重继承,从某种意义上说它不支持,因为你不能将对象的原型基于多个对象的原型; 即你做不到
C.prototype = new B();
C.prototype.constructor = B;
C.prototype.constructor = A;
Run Code Online (Sandbox Code Playgroud)
因为只要你做第三行,你只需要解开第二行.这对instanceof
运营商有影响.
但是,这并不重要,因为只是因为您无法重新定义对象的构造函数两次,您仍然可以将任何方法添加到对象的原型中.所以只是因为你不能做上面的例子,你仍然可以添加任何你想要的C.prototype,包括A和B原型上的所有方法.
许多框架都支持这一点并使其变得简单.我做了很多Sproutcore的工作; 您可以使用该框架
A = {
method1: function(){}
}
B = {
method2: function(){}
}
C = SC.Object.extend(A, B, {
method3: function(){}
}
Run Code Online (Sandbox Code Playgroud)
在这里,我在对象文字定义的功能A
和B
,然后加入到两者的功能性C
,所以C的每一个实例都具有方法1,2,和3.在此特定情况下,该extend
方法(由框架提供)完成所有的繁重设置对象的原型.
编辑 - 在你的评论中,你提出了一个很好的问题,即"如果你使用合成,你如何协调主要对象的范围与主要对象组成的对象的范围".
有很多方法.第一个是简单地传递参数.所以
C.someMethod = function(){
this.a.someMethod(arg1, arg2...);
}
Run Code Online (Sandbox Code Playgroud)
在这里你没有搞乱范围,你只是传递参数.这是一种简单且非常可行的方法.(争论可以来自this
或传入,无论......)
另一种方法是使用javascript 的call
(或apply
)方法,它基本上允许你设置一个函数的范围.
C.someMethod = function(){
this.a.someMethod.call(this, arg1, arg2...);
}
Run Code Online (Sandbox Code Playgroud)
更清楚一点,以下是相同的
C.someMethod = function(){
var someMethodOnA = this.a.someMethod;
someMethodOnA.call(this, arg1, arg2...);
}
Run Code Online (Sandbox Code Playgroud)
在javascript中,函数是对象,因此您可以将它们分配给变量.
该call
调用这里的范围设定someMethodOnA
到this
,这是C的实例