JavaScript的原型继承是老生常谈。因为原型即prototype自己也是对象,因此“原型”继承可认为是一种特殊的“对象式”继承。”对象式“继承是笔者基于本身的理解,所提出的一个名词。本文就着重阐述这两种继承方式的异同之处。app
JavaScript里的原型即prototype是函数的特有属性,原型继承事先得有函数。ide
// 定义函数Foo function Foo(name) { this.name = name; } // 定义Foo的原型 Foo.prototype.say = function() { console.log(this.name, 'say'); }
函数及其原型定义好了,就可使用原型继承了。函数
var foo = new Foo('foo'); foo.say() //foo say foo instanceof Foo; //true。 foo 是Foo的一个实例 foo.__proto__ === Foo.prototype // true
上面用的是new操做符,它实际是经过Object.create工做,其过程以下。因此new实际上是Object.create的便利操做方式。学习
var foo = Object.create(Foo.prototype); foo.name = 'foo' foo.say(); foo instanceof Foo; //true。 foo 是Foo的一个实例 foo.__proto__ === Foo.prototype // true
请打起精神,Object.create(...)接受一个函数原型即object实例,以此实例为“模型”,建立新的object实例。新object实例的__proto__指向前function的prototype,自此新object实例也就拥有了前function prototype的字段和方法。this
既然Foo.prototype自己是object实例,那么咱们是否能够给Object.create(...)传入一个普通的object实例呢?答案是能够的。这就是本文所要表述的“对象式”继承。prototype
开门见山地用code来讲明:code
// 建立对象实例Foo var Foo = { name: 'foo', say: function() { console.log(this.name, 'say'); } } // 以Foo为“模型”,建立新的对象实例foo var foo = Object.create(Foo); foo.__proto__ == Foo;// true // 因此foo也会拥有Foo的字段和方法,这点与原型继承相似。 foo.say(); // foo say. // 可是foo不是Foo的实例。 foo instanceof Foo; // TypeError: Right-hand side of 'instanceof' is not callable
若是Foo是个函数,结果基本是相同的,除了instanceof Foo 会等于false。对象
function Foo() { } Foo.say = function() { console.log(Foo.name, 'say'); } // 以Foo为“模型”,建立新的对象实例foo var foo = Object.create(Foo); foo.__proto__ == Foo;// true // 因此foo也会拥有Foo的字段和方法,这点与原型继承相似。 foo.say(); // Foo say. // 可是foo不是Foo的实例。 foo instanceof Foo; // false
举个不太恰当的例子,若是咱们须要对Math.abs作些“修正”,对于在-1和1之间的数值保持原值。继承
var MMath = Object.create(Math); MMath.abs = function(val) { if (val >= -1 && val <=1) { return val; } return MMath.__proto__.abs(val); } MMath.abs(0.5) // 0.5 MMath.abs(-0.5) // -0.5 MMath.abs(-2) // 2
不论原型继承仍是“对象式”继承,其核心技术是Object.create(...)实现了对象实例的__proto__链。咱们作个简单的实现:ip
Object.myCreate = function(obj) { if (obj instanceof Object || obj === null) { var newObj = {}; Object.setPrototypeOf(newObj, obj) return newObj; } else { throw 'error happens. Should input a object'; } }
function Fooo() {} Fooo.say = function() { console.log(Fooo.name, 'say'); } var myFooo = Object.myCreate(Fooo); myFooo.say(); // Fooo say
自此咱们了解了Object.create(...)的黑魔法,也有助于咱们理解Object.create({})和Oject.create(null)的区别,就是前者的__proto__是个object实例,拥有toString等方法。后者的__proto__是null,它不具备任何方法。在特别注重效率的情景,后者具备优点。
本文提出“对象式”继承的概念,并与原型继承对比,阐述其区别和联系,但愿有助于深化理解。虽然ES6愈来愈普遍应用了,了解函数原型等这些ES3/ES5的概念应该是有助于JS的深刻学习。