于是,我终于停止拖着我的脚,这些年,决定学习JavaScript“正常”。 其中语言设计的最头刮的元素是它的实现继承的。 有在Ruby中的经验,我真的很高兴地看到,封锁和动态类型; 但对我的生活不能弄清楚的好处是什么,从使用其他实例继承对象实例可以了。
Answer 1:
我知道这个答案是晚了3年,但我真的认为目前的答案不提供有关足够的信息如何原型继承比传统继承好 。
首先让我们来看看最常见的参数JavaScript程序员在原型继承(我正在从答案的当前池这些参数)的布防状态:
- 这很简单。
- 它的强大。
- 这导致更小,更冗余代码。
- 它是动态的,因此它是动态语言更好。
现在,这些参数都是有效的,但没有人一直困扰着解释为什么。 这就像对孩子说,学习数学是很重要的。 当然是,但孩子肯定不关心; 你不能说这是重要的作出这样的数学一个孩子。
我认为,与原型继承的问题是,它从JavaScript的角度解释。 我喜欢的JavaScript,但在JavaScript原型继承是错误的。 不同于经典的继承有原型继承的两种模式:
- 原型继承的原型模式。
- 原型继承的构造格局。
不幸的是JavaScript使用原型继承的构造格局。 这是因为JavaScript是创建时, 布兰登·艾克 (JS的创造者),希望它看起来像Java(其中有经典的继承):
我们共推它作为一个小兄弟去渣,像Visual Basic中互补的语言是在当时微软的语系C ++。
因为当人们在JavaScript中使用构造函数,他们认为从构造其他构造继承的这是不好的。 这是错误的。 在原型继承的对象从其他对象继承。 构造函数永远不会进入画面。 这是混淆了大多数人。
从像Java,它具有经典的继承语言的人,因为虽然构造看起来像类不表现得像类得到更加混乱。 正如道格拉斯·克罗克福德说:
这种间接的目的是使语言显得更熟悉的经典训练的程序员,但未能做到这一点,我们可以从非常低的意见Java程序员的JavaScript看看。 JavaScript的构造格局没有上诉古典人群。 它也掩盖JavaScript的真实原型性质。 其结果是,还有谁知道如何有效地运用语言很少程序员。
你有它。 直接从马的嘴。
真正的原型继承
原型继承是所有的对象。 对象继承来自其它对象的属性。 这里的所有都是它的。 有创建一个使用原型继承对象的方法有两种:
- 创建一个全新的对象。
- 克隆现有对象和扩展它。
注:JavaScript的提供了两种方式来克隆对象- 代表团和串联 。 从今往后,我会用“克隆”通过委托专指继承和单词“复制”通过级联专指继承。
少废话。 让我们来看看一些例子。 说我有半径的圆5
:
var circle = {
radius: 5
};
我们可以计算面积和半径的圆的周长:
circle.area = function () {
var radius = this.radius;
return Math.PI * radius * radius;
};
circle.circumference = function () {
return 2 * Math.PI * this.radius;
};
现在,我想创建半径的另一个圆10
。 这样做的一个方法是:
var circle2 = {
radius: 10,
area: circle.area,
circumference: circle.circumference
};
但是JavaScript提供了一个更好的办法- 代表团 。 Crockford的Object.create
功能是用来做:
var circle2 = Object.create(circle);
circle2.radius = 10;
就这样。 你只是做了原型继承在JavaScript中。 是不是很简单? 你把一个对象,克隆它,改变任何你需要,和变戏法似的 - 你拥有属于自己的品牌新的对象。
现在你可能会问,“这是怎么简单?我每次想创建一个新的圈子,我需要克隆circle
和手动指定的区域半径”。 那么解决的办法是用一个函数来完成繁重的你:
function createCircle(radius) {
var newCircle = Object.create(circle);
newCircle.radius = radius;
return newCircle;
}
var circle2 = createCircle(10);
事实上,你可以结合到所有这些单个对象的文字如下:
var circle = {
radius: 5,
create: function (radius) {
var circle = Object.create(this);
circle.radius = radius;
return circle;
},
area: function () {
var radius = this.radius;
return Math.PI * radius * radius;
},
circumference: function () {
return 2 * Math.PI * this.radius;
}
};
var circle2 = circle.create(10);
在JavaScript原型继承
如果在上述方案的通知的create
函数创建的克隆circle
,分配一个新的radius
给它,然后返回它。 这正是一个构造函数在JavaScript:
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.area = function () {
var radius = this.radius;
return Math.PI * radius * radius;
};
Circle.prototype.circumference = function () {
return 2 * Math.PI * this.radius;
};
var circle = new Circle(5);
var circle2 = new Circle(10);
在JavaScript中的构造模式是倒置的原型模式。 而不是创建一个对象,你创建一个构造。 在new
关键字结合this
在构造函数中指针的克隆prototype
构造的。
听起来很混乱? 这是因为在JavaScript中的构造模式不必要地复杂的事情。 这是大多数程序员觉得难以理解。
而不是从其他对象继承对象的思想,他们认为从构造其他构造继承,然后成为心乱如麻。
还有就是为什么应避免在JavaScript的构造模式等原因一大堆。 你可以在我的博客文章在这里阅读他们: 构造函数原型VS
那么,什么是原型继承过经典的继承好处? 让我们通过最常见的论据吧,并解释原因 。
1.原型继承是简单
CMS在他回答说:
在我看来原型继承的主要优点是它的简单性。
让我们来考虑一下我们只是做了。 我们创建了一个对象circle
其其半径5
。 然后,我们克隆了它,并给克隆的半径10
。
因此,我们只需要两件事情,使原型继承工作:
- 一种方法来创建一个新的对象(例如对象文本)。
- 的一种方式扩展现有的对象(例如
Object.create
)。
相比之下经典的继承要复杂得多。 在经典的继承您有:
- 类。
- 宾语。
- 接口。
- 抽象类。
- final类。
- 虚基类。
- 构造函数。
- 析构函数。
你的想法。 问题的关键是,原型继承更容易理解,更容易实现,也更容易推理。
正如史蒂夫·耶格把它放在他的经典博客文章“ 肖像N00B的 ”:
元数据是任何一种描述或别的什么模式。 在你的代码中的注释只是AA计算的自然语言描述。 是什么让元数据元数据是,它不是绝对必要的。 如果我有一些血统文书一只狗,我失去的文书工作,我仍然有一个完全有效的狗。
在同样的意义类只是元数据。 类并不严格要求继承。 然而,一些人(通常n00bs)班找到更舒适的工作。 这让他们安全的错觉。
好了,我们也知道,静态类型只是元数据。 他们是一种特殊的注释针对2种读者:程序员和编译器。 静态类型讲一个故事有关的计算,大概是为了帮助这两个读者群理解程序的意图。 但静态类型可以扔掉在运行时,因为最终他们只是程式化的意见。 他们就像血统文书:它可能使某种不安全的人格类型更幸福对他们的狗,但狗肯定不关心。
正如我前面所说,班会给人一种虚假的感觉。 例如,你得到太多NullPointerException
S IN的Java甚至当你的代码是完全清晰可辨。 我觉得经典的继承往往会获得编程的方式,但也许这只是Java的。 Python有一个惊人的经典的继承系统。
2.原型继承是强大的
谁来自一个古典背景大多数程序员认为,经典的继承比原型继承,因为它有更强大:
- 私有变量。
- 多重继承。
这种说法是错误的。 我们已经知道,JavaScript的支持通过关闭私有变量 ,但对于多重继承? 在JavaScript对象只有一个雏形。
事实是,原型继承支持从多个原型继承。 原型继承仅仅意味着一个对象从另一个对象继承。 实际上有实现原型继承两种方式 :
- 代表团或差分继承
- 克隆或继承的串连
是JavaScript的只允许对象委托给一个其他对象。 但是它可以让你拷贝对象任意数量的属性。 例如_.extend
不只是这个。
当然,许多程序员不认为这是正确的,因为继承instanceof
和isPrototypeOf
否则说。 然而,这可以通过存储经由级联从原型继承的每个对象上的原型的阵列可以容易地补救:
function copyOf(object, prototype) {
var prototypes = object.prototypes;
var prototypeOf = Object.isPrototypeOf;
return prototypes.indexOf(prototype) >= 0 ||
prototypes.some(prototypeOf, prototype);
}
因此,原型继承是一样经典的继承一样强大。 事实上,它比传统的继承更强大,因为在原型继承,你可以用手去拿哪些属性复制和哪些属性从不同的原型省略。
在经典的继承,这是不可能的(或至少很困难),选择您要继承的属性。 他们用虚基类和接口解决钻石问题 。
在JavaScript但你很可能永远听不到钻石的问题,因为你可以精确地控制你希望哪些属性继承并从中原型。
3.原型继承较少冗余
这点是一个小更难以解释,因为经典的继承并不必然导致更多的冗余代码。 事实上继承,无论是古典还是原型,用来减少代码冗余。
有一种说法可能是与经典的继承大多数编程语言都是静态类型,并要求用户显式声明的类型(不像哈斯克尔具有隐含的静态类型)。 因此,这将导致更多的冗长的代码。
Java是臭名昭著的这种行为。 我清楚地记得鲍勃·奈斯特龙在他的博客文章提到了以下轶事普拉特解析器 :
你要爱Java的“请签字一式四份”这里的官僚层次。
同样,我认为这不仅是因为Java的吸这么多。
一个有效的论据是不具有经典的继承支持多重继承所有语言。 Java的再次浮现在脑海。 是Java有接口,但是这还不够。 有时候,你真的需要多重继承。
由于原型继承允许多重继承,代码需要如使用原型继承写,而不是在其中有经典的继承,但没有多重继承的语言多继承少冗余。
4.原型继承是动态
其中一个原型继承的最重要的优势是,你可以在创建之后,他们到原型添加新的特性。 这可以让你的新方法添加到它会自动地提供给所有委托给原型对象的原型。
这不是经典的继承可能的,因为一旦创建一个类,你不能在运行时修改它。 这可能是在经典的继承原型继承的一个最大的优势,它应该是在顶部。 不过我喜欢把最好的留结束。
结论
原型继承事宜。 重要的是要教育为什么放弃原型继承的构造格局有利于原型继承的原型模式的JavaScript程序员是非常重要的。
我们需要正确启动教学的JavaScript,这意味着展示了如何使用原型模式,而不是构造模式来编写代码的程序员新。
它不仅将是更容易使用原型模式解释原型继承,但它也将做出更好的程序员。
如果你喜欢这个答案,那么你也应该阅读“我的博客文章为什么原型继承事项 ”。 相信我,你会不会感到失望。
Answer 2:
请允许我其实回答这个问题直列。
原型继承具有以下优点:
- 因为继承是因为它是在环境的动态,是更适合于动态语言。(是否适用于JavaScript的应该是显而易见的在这里。)这允许您快速地做事情上飞象定制类,而大量的基础设施代码。
- 这是比较容易实现比传统的类/对象二分法方案原型对象的方案。
- 它不需要像“元类”的对象模型周围的复杂尖锐的边缘需要(我从来元类我喜欢...对不起!)或“特征值”等。
它具有以下缺点但是:
- 键入检查原型的语言是不可能的,但它是非常,非常困难。 典型语言的大多数“类型检查”是纯粹的运行时“鸭打字”式的检查。 这是不适合所有环境。
- 这同样是很难做的事情一样通过静态的(或者,经常,甚至是动态的!)分析方法,优化调度。 它可以 (我强调: 可 )是非常低效的变得非常容易。
- 类似地创建对象可以是(并且通常是)在一个原型语言远远低于它可以在更常规的类/对象二分法方案。
我想,你可以在上面的字里行间,并拿出相应的优势和传统类/对象方案的缺点。 有,当然,更多的在每个领域,所以我会离开休息了其他人回答。
Answer 3:
IMO原型继承的主要优点是它的简单性。
语言的原型性质可以混淆谁是经典训练的人,但事实证明,其实这是一个非常简单和强大的概念, 差分继承 。
你并不需要进行分类 ,你的代码是较小的,不太冗余,对象从其他普通对象继承。
如果您认为prototypically你很快就会发现,你不需要类...
原型继承会在不久的将来更受欢迎,在ECMAScript的第5版规范推出Object.create
方法,它可以让你产生从另一个在一个非常简单的方法继承了一个新的对象实例:
var obj = Object.create(baseInstance);
这一新标准的版本是由所有的浏览器厂商实现,我想我们将开始看到更多的纯原型继承...
Answer 4:
实在是没有太多的两种方法之间做出选择。 其基本思路把握的是,当JavaScript引擎被赋予一个对象的属性来读,它首先检查实例,如果该属性缺失,它会检查了原型链。 这里是表示原型和古典之间的差异的例子:
原型
var single = { status: "Single" },
princeWilliam = Object.create(single),
cliffRichard = Object.create(single);
console.log(Object.keys(princeWilliam).length); // 0
console.log(Object.keys(cliffRichard).length); // 0
// Marriage event occurs
princeWilliam.status = "Married";
console.log(Object.keys(princeWilliam).length); // 1 (New instance property)
console.log(Object.keys(cliffRichard).length); // 0 (Still refers to prototype)
古典与实例方法 (低效的,因为每个实例存储它自己的属性)
function Single() {
this.status = "Single";
}
var princeWilliam = new Single(),
cliffRichard = new Single();
console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 1
高效的经典
function Single() {
}
Single.prototype.status = "Single";
var princeWilliam = new Single(),
cliffRichard = new Single();
princeWilliam.status = "Married";
console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 0
console.log(cliffRichard.status); // "Single"
正如你所看到的,因为它是可以操纵的古典风格宣称“类”的原型,确实使用原型继承没有任何好处。 它是经典方法的一个子集。
Answer 5:
Web开发:原型继承与经典传承
http://chamnapchhorn.blogspot.com/2009/05/prototypal-inheritance-vs-classical.html
古典Vs的原型继承 - 堆栈溢出
古典Vs的原型继承