JavaScript-原型继承工作原理


【翻译】JavaScript原型继承工作原理

【翻译】JavaScript原型继承工作原理



Javascript – 如何真正实现原型链继承

本文转载自:众成翻译
译者:Daguo
链接:http://www.zcfy.cc/article/1337
原文:http://blog.vjeux.com/2011/javascript/how-prototypal-inheritance-really-works.html

在网上的很多地方我们可以得知javascript是基于原型链继承的,其实Javascript只提供一种特殊的方法来实现原型链继承,就是通过new操作。但是很多解读都令人难以理解,这篇文章旨在说明到底什么是原型链继承以及如何在Javascript中真正地运用它。

原型链继承的定义

当你读到有关于Javascript原型链继承的内容时,经常看到这样的定义:

当对象存取一个属性时,Javascript会沿着原型链向上查询直到找到要存取的属性名(或原型链顶端)Javascript Garden

许多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操作符必须指向一个函数。
  • 我们需要在一些地方用到对象的方法,由于我们使用的是一门原型语言,可将方法放在函数的原型属性上。

new操作符创建函数F和参数arguments : new F(arguments…),进行简单的三步操作:

  1. 创建一个实例,它是一个空对象,其__proto__属性被设为F.prototype。
  2. 实例初始化,将函数F的参数传递到实例上,F的this(上下文)被设为指向实例。
  3. 返回实例

现在我们明白new操作符做了什么,我们可以在Javascript中实践它。

     function New (f) {
/*1*/  var n = { '__proto__': f.prototype };
       return function () {
/*2*/    f.apply(n, arguments);
/*3*/    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函数。

Object.create = function (parent) {
  function F() {}
  F.prototype = parent;
  return new F();
};

这看起来非常奇怪,但它的原理其实非常简单。将创建的一个新对象的prototype属性设置为你想要继承的对象,如果允许使用__proto__,那么可以改写为:

Object.create = function (parent) {
  return { '__proto__': parent };
};

下面就是用真正的原型链继承来实现的Point例子的继承过程。

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 and __proto__) 有一些缺点:

  • 没有标准: __proto__不是标准甚至是被反对的. 包括通用的Object.create和Douglas Crockford实践都没有确定的标准形式。
  • 没有优化: Object.create比起new操作方式显得更加笨重,这一点被证明于10 times slower.
参考阅读:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值