Javascript – 原型继承真正的工作方式(by Vjeux)
2014-05-18 20:34
309 查看
作者:Vjeux
原文链接:点击打开链接
JavaScript有原型继承,在几乎任意的关于web开发的文章中我们在都能读到。然而,JavaScript仅仅提供了默认的用new操作符实现特定情况下的原型继承。因此,大多数的解释真的让人感到很困惑。这篇文章的目的在于澄清原型继承到底是什么以及如何在JavaScript中真正的应用她 。
原型继承的定义
当你在读原型继承的定义的时候,她经常是这样子的:
当你想获取一个对象的属性的时候,JavaScript会沿着原型链往上直到她找到该名字的属性。
绝大多数的JavaScript用了__proto__来表示在原型链上得下一个对象,我们会在这篇文章中看到__proto__和prototype之间的差别。
注:__proto__并不是标准所要求,而且也不应该出现在你的代码里。它出现在文章中是用来解释JavaScript原型如何工作。
下面的代码展示了JavaScript引擎如何检索属性(仅供阅读):
举一个通常的示例:一个2D的坐标点。这个点有两个坐标值:x、y和一个方法:print。
应用前面写的原型继承的定义,我们将实现一个点对象,她有三个属性: x, y 和print。为了创建一个新的点,我们仅仅需要用__proto__创建一个新对象并将其设置给Point。
JavaScript怪异的原型继承
令人不解的是大多数的人在用这个定义来讲解JavaScript原型继承的时候并不给出这组代码。相反的他们一般给出这样的代码:
这和我上面给出的代码是完全不同的。Point现在是一个函数,并且用了一个prototype属性,new操作符。这究竟是在搞什么?
new是如何工作的
Brendan Eich想让JavaScript看起来像面向对象编程语言那样,比如Java 和 C++。为此,我们用new来创建一个类的实例。因此他为JavaScript写了new操作符。
*C++有构造函数的概念,她初始化了实例的属性。因此,new操作符的目标必须是一个函数。
*我们需要把方法放在对象的某个地方。因为我们正在研究一个原型语言,因此让我们把它放在函数的prototype属性上。
new操作符需要一个F函数和参数:new F(arguments...)。
她简单的执行三步工作:
1、创建类的实例。这个实例是一个空对象,它的__proto__属性被设置为F.prototype。
2、初始化这个实例。函数F被传入参数调用并且this被设置为这个实例。
3、返回该实例。
现在我们理解了new操作符做了哪些事情,我们也可以在JavaScript中实现她:
做个检测她是否起作用的测试:
真正的JavaScript中的原型继承
在Javascript specifications中仅仅给出了new操作符操作了什么。然而Douglas Crockford发现了一种方式来扩展new以实现真正的原型继承。他写了Object,create函数(链接:http://javascript.crockford.com/prototypal.html)。
她看起来很奇怪但是真的很简单。她只是创建了一个新对象并且把他的prototype属性设置为任何你想要的。如果允许使用__proto__,她也可以写成这样:
下面的代码是用真正的原型继承实现的我们的点的例子:
结论
我们已经了解了原型继承是很么并且在JavaScript中用特定的方式实现了她。然而,真正原型继承的应用(Object.create 和 __proto__)还有一些漏洞:
1、非标准: __proto__并非标准规定甚至是被弃用的。而且原生的Object.create和Douglas Crockford的实现并不完全等同。
2、非最佳:Object.create (native or custom) 还没有像new结构那样的被最优化,她会慢十倍甚至更多。
额外的福利
如果你能从下面的图中(from the ECMAScript standard) 理解原型继承到底是怎么工作的,你就获得了一份免费的“甜圈”!
原文链接:点击打开链接
JavaScript有原型继承,在几乎任意的关于web开发的文章中我们在都能读到。然而,JavaScript仅仅提供了默认的用new操作符实现特定情况下的原型继承。因此,大多数的解释真的让人感到很困惑。这篇文章的目的在于澄清原型继承到底是什么以及如何在JavaScript中真正的应用她 。
原型继承的定义
当你在读原型继承的定义的时候,她经常是这样子的:
当你想获取一个对象的属性的时候,JavaScript会沿着原型链往上直到她找到该名字的属性。
绝大多数的JavaScript用了__proto__来表示在原型链上得下一个对象,我们会在这篇文章中看到__proto__和prototype之间的差别。
注:__proto__并不是标准所要求,而且也不应该出现在你的代码里。它出现在文章中是用来解释JavaScript原型如何工作。
下面的代码展示了JavaScript引擎如何检索属性(仅供阅读):
function getProperty(obj, prop) { if (obj.hasOwnProperty(prop)) return obj[prop] else if (obj.__proto__ !== null) return getProperty(obj.__proto__, prop) else return undefined }
举一个通常的示例:一个2D的坐标点。这个点有两个坐标值:x、y和一个方法:print。
应用前面写的原型继承的定义,我们将实现一个点对象,她有三个属性: x, y 和print。为了创建一个新的点,我们仅仅需要用__proto__创建一个新对象并将其设置给Point。
var Point = { x: 0, y: 0, print: function () { console.log(this.x, this.y); } }; var p = {x: 10, y: 20, __proto__: Point}; p.print(); // 10 20
JavaScript怪异的原型继承
令人不解的是大多数的人在用这个定义来讲解JavaScript原型继承的时候并不给出这组代码。相反的他们一般给出这样的代码:
function Point(x, y) { this.x = x; this.y = y; } Point.prototype = { print: function () { console.log(this.x, this.y); } }; var p = new Point(10, 20); p.print(); // 10 20
这和我上面给出的代码是完全不同的。Point现在是一个函数,并且用了一个prototype属性,new操作符。这究竟是在搞什么?
new是如何工作的
Brendan Eich想让JavaScript看起来像面向对象编程语言那样,比如Java 和 C++。为此,我们用new来创建一个类的实例。因此他为JavaScript写了new操作符。
*C++有构造函数的概念,她初始化了实例的属性。因此,new操作符的目标必须是一个函数。
*我们需要把方法放在对象的某个地方。因为我们正在研究一个原型语言,因此让我们把它放在函数的prototype属性上。
new操作符需要一个F函数和参数:new F(arguments...)。
她简单的执行三步工作:
1、创建类的实例。这个实例是一个空对象,它的__proto__属性被设置为F.prototype。
2、初始化这个实例。函数F被传入参数调用并且this被设置为这个实例。
3、返回该实例。
现在我们理解了new操作符做了哪些事情,我们也可以在JavaScript中实现她:
function New (f) { var n = { '__proto__': f.prototype }; return function () { f.apply(n, arguments); return n; }; }
做个检测她是否起作用的测试:
function Point(x, y) { this.x = x; this.y = y; } Point.prototype = { print: function () { console.log(this.x, this.y); } }; var p1 = new Point(10, 20); p1.print(); // 10 20 console.log(p1 instanceof Point); // true var p2 = New (Point)(10, 20); p2.print(); // 10 20 console.log(p2 instanceof Point); // true
真正的JavaScript中的原型继承
在Javascript specifications中仅仅给出了new操作符操作了什么。然而Douglas Crockford发现了一种方式来扩展new以实现真正的原型继承。他写了Object,create函数(链接:http://javascript.crockford.com/prototypal.html)。
Object.create = function (parent) { function F() {} F.prototype = parent; return new F(); };
她看起来很奇怪但是真的很简单。她只是创建了一个新对象并且把他的prototype属性设置为任何你想要的。如果允许使用__proto__,她也可以写成这样:
Object.create = function (parent) { return { '__proto__': parent }; };
下面的代码是用真正的原型继承实现的我们的点的例子:
var Point = { x: 0, y: 0, print: function () { console.log(this.x, this.y); } }; var p = Object.create(Point); p.x = 10; p.y = 20; p.print(); // 10 20
结论
我们已经了解了原型继承是很么并且在JavaScript中用特定的方式实现了她。然而,真正原型继承的应用(Object.create 和 __proto__)还有一些漏洞:
1、非标准: __proto__并非标准规定甚至是被弃用的。而且原生的Object.create和Douglas Crockford的实现并不完全等同。
2、非最佳:Object.create (native or custom) 还没有像new结构那样的被最优化,她会慢十倍甚至更多。
额外的福利
如果你能从下面的图中(from the ECMAScript standard) 理解原型继承到底是怎么工作的,你就获得了一份免费的“甜圈”!
相关文章推荐
- javascript prototype的深度探索不是原型继承那么简单第1/3页
- JavaScript原型继承小记
- javascript 用原型继承来实现对象系统
- JavaScript 原型与继承说明
- JavaScript 原型 继承
- 如何利用javascript中的原型实现继承
- JavaScript中的对象和原型链,函数继承(转)
- Javascript--原型链实现继承
- Javascript--原型链实现继承
- JavaScript原型 继承
- JavaScript继承之闭包原型继承法
- Javascript 原型和继承(Prototypes and Inheritance)
- Javascript 原型和继承(Prototypes and Inheritance)
- Javascript对象原型prototype和继承
- javascript 基于原型的知识--如何实现继承
- javascript prototype的深度探索不是原型继承那么简单第1/3页
- Javascript 原型和继承(Prototypes and Inheritance)
- 浅析Javascript原型继承 推荐第1/2页
- Javascript 原型和继承(Prototypes and Inheritance)
- 再谈Javascript原型继承