Javascript的原型继承是如何工作的

原文: http://blog.vjeux.com/2011/javascript/how-prototypal-inheritance-really-works.html

在网络到处可以看到Javascript是原型继承,但Javascript只提供了使用new关键字来实现原型继承,所以很多文章读后有诸多的疑问。这篇文章目的就是要说明Javascript的原型继承是如何工作的。

原型继承定义

原型继承大多被定义为:

当访问对象的属性时,Javascript会向上遍历原型链,直到找到相应的属性  JavascriptGarden

许多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
}

使用Point做个例子,它有两个属性x, y并有一个方法print。在使用原型继承之前,先创建一个Point,并把它做为属性__proto__的值传给一个新的对象。

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怪异的原型继承

许多人在讲原型继承时并没有给出上面的代码,却都给出下面类似的代码:

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 是如何工作的

BrendanEich 想让Javascript像传统面向对象语言(Java, C++)一样使用new来创建一个对象。所以他在Javascript中定义了关键字new。

  • 在C++在有构造器的概念,构造器会初始化实例的一些属性,所以构造器必须是一个函数。
  • 因为Javascript是一种原型语言,所以把prototype做为函数的一个属性。

使用new,需要函数名F和参数:new F(arguments…)。这个过程做了三件事情:

  1. 创建了一个实例。这个实例是一个具有__proto__属性的空对象,并且__proto__指向F.prototype
  2. 初始化实例。将arguments 和 this赋予函数F。
  3. 返回这个实例。

用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只有关键字new,并没有与继承相关的关键字,但Douglas Crockford给出了一种实现原型继承的方法。代码如下:

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

很奇怪吧,非常简单,就是创建了一个新对象,并把parent赋给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

结论

我们明白了什么是原型继承并且是如何工作的。

但使用原型继承(Object.create()和__proto__)有缺陷:

  • 非标准:__proto__并非标准,并且已经废弃。还有,Javascript中Object.create的实现和Douglas Crockford的实现并不完全一样。
  • 没有优化:Object.create(本地实现和用户实现)都没有new的效率高,要慢10倍以上。

其它文章:


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值