我已经阅读了以下QAs,所有这些都使用原型继承来模拟经典继承。
Good Example of JavaScript's Prototype-Based Inheritance
Using inheritance patterns in JavaScript
在野外没有一个原型继承的工作实例吗?模拟生命形式,也许?除了编程语言创建或未充分解决的问题之外,哪些问题会受益于原始的原型继承?
答案 0 :(得分:6)
继承是继承,因此您可以从中获得相同的基本功能。
JavaScript中原型继承的一个好处是允许动态运行时添加新方法或更改旧方法可用于所有对象(不添加每个对象的内存损失)。
这可能很危险,尤其是当内置方法(如String或Object)以颠覆(或前瞻性)方式覆盖其内置方法时。
String.prototype.replace = function () {
return 'hahaha';
};
但是当某些浏览器或库的实现功能不足或性能滞后时,它可能会很强大。
它还有助于库的模块化,可扩展性和改进。如果您包含某人的库并发现他们的特定方法的实现可以更好地优化,您可以在不篡改它的情况下放入他们的代码,同时仍然能够改进它或为其添加功能并使其外部定义的所有对象受益。库(至少在您开始将其添加到原型中时)。库甚至可以根据用户的喜好来交换实现(虽然它可能会干扰使用该方法的其他代码,但通常不是一个好主意),或者让它们动态定义他们想要使用的方法的名称。
原型行为即使在“类”中也会发挥作用,因为你可以利用直接存储在对象上的便利性(尽管在这种情况下确实会增加内存,并且可能更好地创建一个新类 - 但它仍然很方便)。
function Dog (type) {
if (type === 'poodle') {
this.bark = function () {
alert('(yapyapyap)');
};
}
}
Dog.prototype.bark = function () {
alert('(woof)');
};
var muffy = new Dog('poodle');
muffy.bark(); // '(yapyapyap)'
var rover = new Dog();
rover.bark(); // '(woof)'
原型是可以在JavaScript的原型方法中动态更改或交换的事实,这使得您可以在运行时动态创建新类,这与一些更传统的语言不同,至少提供一些更简洁的表达性:
function Creature () {}
Creature.prototype.respire = function () { return 'oooooh'; };
function createClass (o, f) {
f = f || function f () {}
f.prototype = (typeof o === 'function') ? o.prototype : o.constructor.prototype;
f.prototype.constructor = f;
return f;
}
var animals = ['Dog', 'Tiger', 'Lion', 'Frog', 'Kangaroo'];
animals.forEach(function (animal) {
window[animal] = createClass(Creature);
});
var rover = new Dog();
最后,您可以通过借用您需要的内容来避免严格的is-a层次结构,同时仍然利用可继承的功能:
function createMixinClass (old, constructor, newMethods) {
if (typeof constructor === 'object') {
newMethods = constructor;
constructor = null;
}
var proto = old.prototype, constructor = constructor || function () {};
for (var m in proto) {
constructor.prototype[m] = proto[m];
}
for (var method in newMethods) {
if (!newMethods[method]) {
delete constructor.prototype[method];
}
else {
constructor.prototype[method] = newMethods[method];
}
}
return constructor;
}
var Cat = createMixinClass(Dog, {bark:null, meow: function () {alert('meow');}});
var kitty = new Cat();
简而言之,我认为没有什么可以让你处理新的问题,但它提供了更大的灵活性,特别是一些可重用的实用程序很方便。
答案 1 :(得分:2)
它不是"教学"例如,一个真实的世界"使用原型继承是jQuery插件。 $.fn
实际上是魔法jQuery集合的原型,jQuery插件为其添加方法,以便为任何jQuery集合添加功能。
答案 2 :(得分:2)
在野外有很多原型遗传的例子。值得注意的是,jQuery使用它。每次进行jQuery选择时,都使用委托原型来继承jQuery方法。它也常用于各种Web应用程序,包括Abode的Creative Cloud平台,雅虎的许多产品等......
事实上,JavaScript中经典继承的每一个实现都实际上使用原型继承来模仿经典继承 - 这对于更熟悉经典继承而不是原型继承的程序员来说是必要的。原型继承是如此灵活,以至于使用原型继承来模仿经典继承的特征是微不足道的。反之则不然。
原型继承意味着对象直接从另一个对象继承。这是一个例子:
var switchProto = {
isOn: function isOn() {
return this.state;
},
toggle: function toggle() {
this.state = !this.state;
return this;
},
state: false
},
switch1 = Object.create(switchProto),
switch2 = Object.create(switchProto);
将Object.create()
调用放在工厂函数中以使对象实例化更方便是很常见的。
经典继承存在许多与原型继承不存在的问题,例如:
紧耦合。继承是OO设计中最紧密的耦合。后裔课程对他们的祖先课程有着深入的了解。
不灵活的层次结构(也就是必要的重复)。单父层次结构很少能够描述所有可能的用例。最终,所有层次结构对于新用途都是“错误的” - 这是一个需要重复代码的问题。
多重继承很复杂。通常需要从多个父级继承。该过程非常复杂,其实现与单继承的过程不一致,这使得它更难以阅读和理解。
脆弱的架构。由于紧密耦合,通常很难用“错误”设计重构类,因为现有的许多功能取决于现有设计。
大猩猩/香蕉问题。通常,您不希望继承父级的某些部分。子类化允许您覆盖父级的属性,但它不允许您选择要继承的属性。
要了解原型继承如何解决这些问题,您应该首先了解有两种不同类型的原型继承。 JavaScript支持:
团。如果在实例上找不到属性,则会在实例的原型上搜索该属性。这使您可以在许多实例之间共享方法,为您提供免费的 flyweight模式。
级联。向对象动态添加属性的功能使您可以将任何属性从一个对象自由复制到另一个对象,所有这些属性可以一起或有选择地复制。
您可以结合两种形式的原型继承来实现非常灵活的代码重用系统。实际上非常灵活,用原型实现经典继承是微不足道的。反之则不然。
原型继承允许您在古典语言中找到的大多数重要功能。在JavaScript中,闭包和工厂函数允许您实现私有状态,并且功能继承可以很容易地与原型结合,以便添加支持数据隐私的mixin。
松耦合。实例永远不需要直接引用父类或原型。可以存储对对象原型的引用,但不建议,因为这会促进对象层次结构中的紧密耦合 - 这是经典继承的最大缺陷之一。
平面层次结构。使用原型OO使继承层次结构保持平坦是微不足道的 - 使用连接和委托,您可以拥有单个级别的对象委派和单个实例,而不会引用父类。
琐碎的多重继承。从多个祖先继承就像使用串联组合多个原型的属性以形成新对象或新对象的新委托一样简单。
灵活的架构。由于您可以选择性地继承原型OO,因此您不必担心“错误的设计”问题。新类可以从任何源对象组合继承任何属性组合。由于层次结构扁平化的容易性,一个地方的变化不一定会导致整个长链后代对象的涟漪。
没有更多的大猩猩。选择性继承消除了大猩猩香蕉问题。
我不知道经典继承优于原型继承的任何优势。如果有人知道,请赐教。
答案 3 :(得分:1)
Self Language Blog上有一些有趣的链接,包括一些视频。