Javascript – 原型继承真正的工作方式(by Vjeux)

作者:Vjeux

原文链接:点击打开链接

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 <wbr>– <wbr>原型继承的真正工作方式(by <wbr>Vjeux)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值