我来自面向对象语言的课程,最近我一直在学习那些花哨的动态语言(JavaScript、Python 和 Lua),我想要一些关于如何在这些语言中使用 OO 的技巧。了解这种方法的缺陷和缺点以及与传统 OO 相比的优势将很有用。
我得到的一般概念是,基于原型的 OO 基本上是使用对象进行编程,但没有关于如何使用它们的标准,而在普通 OO 中,有一种固定的预定义方式来制作和使用对象。
总而言之,这种方法的优点、缺点和丑陋的部分是什么?
我来自面向对象语言的课程,最近我一直在学习那些花哨的动态语言(JavaScript、Python 和 Lua),我想要一些关于如何在这些语言中使用 OO 的技巧。了解这种方法的缺陷和缺点以及与传统 OO 相比的优势将很有用。
我得到的一般概念是,基于原型的 OO 基本上是使用对象进行编程,但没有关于如何使用它们的标准,而在普通 OO 中,有一种固定的预定义方式来制作和使用对象。
总而言之,这种方法的优点、缺点和丑陋的部分是什么?
基于原型的 OO 不适合静态类型检查,有些人可能认为这是一件坏事或丑陋的事情。基于原型的 OO确实具有创建新对象的标准方法,即克隆和修改现有对象。还可以建工厂等等。
我认为人们最喜欢(“好”)的是基于原型的 OO 非常轻巧且灵活,提供了非常高的功率重量比。
有关如何使用基于原型的 OO 的提示,一个很好的起点是关于The Power of Simplicity的原始 Self 论文。
为了节省带宽,这里是我关于“如何在 JavaScript 中模拟“类”?(有或没有第三方库)”的答案的链接。它包含更多参考资料和示例。
简短的回答:JavaScript 原型OO的核心是委托。在这种 OOP 风格中,同一个“类”的不同对象可以将方法和属性的处理委托给同一个原型(通常是第三个对象):
var foo = {
property: 42,
inc: function(){
++this.counter;
},
dec: function(){
--this.counter;
}
};
// Note: foo does not define `counter`.
让我们为以 foo 作为原型的对象创建一个构造函数。实际上,所有未处理的都将委托给 foo。
var Bar = function(){
this.counter = 0;
};
Bar.prototype = foo; // This is how we set up the delegation.
// Some people refer to Bar (a constructor function) as "class".
var bar = new Bar();
console.log(bar.counter); // 0 --- Comes from bar itself
console.log(bar.property); // 42 --- Not defined in bar, comes from foo
bar.inc(); // Not defined in bar => delegated to foo
bar.inc();
bar.dec(); // Not defined in bar => delegated to foo
// Note: foo.inc() and foo.dec() are called but this === bar
// that is why bar is modified, not foo.
console.log(bar.counter); // 1 --- Comes from bar itself
让我们inc()
直接在 bar 上定义:
bar.inc = function(){
this.counter = 42;
};
bar.inc(); // Defined in bar => calling it directly.
// foo.inc() is not even called.
console.log(bar.counter); // 42 --- Comes from bar
设置单一继承链:
var Baz = function(){
this.counter = 99;
};
Baz.protype = new Bar();
var baz = new Baz();
console.log(baz.counter); // 99
baz.inc();
console.log(baz.counter); // 100
console.log(baz instanceof Baz); // true
console.log(baz instanceof Bar); // true
console.log(baz instanceof Object); // true
整齐,嗯?
在担心如何在 JavaScript 中模拟基于类的继承之前,请快速阅读JavaScript 中的原型继承。
经典继承在灵活性方面存在固有缺陷,因为我们说“这个对象属于这种类型,没有其他类型”。一些语言引入了多重继承来缓解这种情况,但多重继承有其自身的缺陷,因此纯组合优于继承(在静态类型语言中,继承是运行时而不是编译时机制)的好处变得很明显。
将组合的概念提升到这个“纯”级别,我们可以完全消除经典继承以及静态类型。通过在运行时组合对象并将它们用作蓝图(原型方法),我们无需担心通过继承过于紧密地装箱对象,也无需为多重继承方法中固有的问题而负担。
所以原型意味着更灵活的模块开发。
当然,说不用静态类型就很容易开发是另一回事。海事组织,它不是。
好的,首先,原型模型在现实中并没有什么不同。Smalltalk 使用类似的方案。该类是具有类方法的对象。
从类POV来看,一个类实际上是具有相同数据、所有相同方法的对象的等价类;您可以将向原型添加方法视为创建新子类。
实现很简单,但是很难进行有效的类型检查。