在网页上咱们随处可见说javascript是一种原型继承。然而javascript仅仅默认地在使用new操做符的这种特殊状况下提供了原型继承。所以,许多js的解读都让人感到很迷惑。 这篇文章用于阐述什么是原型继承以及如何在js中使用原型继承。javascript
原型继承的目的:在 js 中实现一种对象之间的资源(对象属性)共享。html
关于js的原型继承,你常会看到以下定义:java
当访问一个对象的属性时,js会沿着原型链向上遍历直到寻找到匹配的属性。编程
大多数js实现使用 proto 属性来表明原型链中的下一层对象。在这篇文章中咱们会看到 proto 和 prototype 究竟有什么不一样之处。app
注意: proto 是一种非标准表示方式,你不该该将其用在你的代码中。在这篇文章中咱们用它来描述js继承是如何实现的。编程语言
下面代码显示了js 引擎如何检索属性(读取)。函数
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。
使用以前咱们所写的原型继承方法,咱们将建立一个 Point 对象以及它的三个属性: x, y, 和 point。要建立一个新的点,咱们只须要使用设置在 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
复制代码
让人迷惑的是每一个教授js原型继承的人都给了这样的定义却没有给如上的代码。他们所给的代码像下面这样:优化
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 操做符。这是什么鬼东西?ui
Brendan Eich 想要将 Javascript 设计成一种传统的面向对象的编程语言,如同 Java h和 C++ 那样。在这些语言中,咱们使用 new 操做符来建立一个类的实例。因此他为 Javascript 添加了 new 操做符。
C++ 有构造函数( constructor )的概念,用于初始化实例属性。所以,new 操做符必须的做用对象必须是一个函数。
咱们必须把对象的方法放在某一处。因为咱们使用的是一种原型语言,让咱们把它置于函数的原型属性中。
new 操做符建立一个带参数的 F 函数: new F(arguments...)。经过以下三步来实现:
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
复制代码
js规范仅为咱们提供了 new 操做符可使用。Douglas Crockford 找到了一种利用 new 操做符来实现原型继承的方法!他创造了 Object.create 函数。
Object.create = function (parent) {
function F() {}
F.prototype = parent;
return new F();
};
复制代码
这种写法看起来很奇怪但它的工做原理很简单。它仅仅根据设置在这个函数上的原型来建立了一个新的对象。若是咱们可使用 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
复制代码
咱们看到了究竟什么是原型继承而且看到了 js 仅仅是用了一种特别的方法来实现它。
然而,使用原型继承(Object.create and proto)也存在一些缺点:
若是你可以经过下面的图理解原型继承是如何工做的,Congratulations!