说到原型,不得不提到原型链,js中无论是对象仍是方法(也是对象)都有个隐藏属性_proto_,来表示原型链的下一个指向,通常对象是指向Object.prototype,方法是指向Function.prototype,构造函数new出来的对象指向想构造函数的prototype
由于原型链的存在,当前对象或者方法能够共用原型链上的上级属性和方法es6
var obj = {}; obj.toString() //[object Object]
obj对象上是没有toString方法的,由于obj._proto_指向Object.prototype,具体上是调用Object.prototype方法:Object.prototype.toString.apply(obj)app
function Foo() { } Foo.toString();//function Foo() {}
方法的原型链是Foo->Function.prototype->Object.prototype,因此Foo调用toString方法是引用Function上的toString方法,具体上是调用Function.prototype方法:Function.prototype.toString.apply(obj)函数
有时候咱们为了继承父类(其实js并无类这个概念),会经过原型继承去继承父类的一些方法,在此以前,先简单叙述下经过构造函数实例化一个对象的过程,好比如下建立一个obj对象,this
var obj = new Object();
因此当es5继承方法时,能够选择原型继承,经过修改prototype的值,如:
ES5的状况:es5
var Father = function(name) { this.name = name; } Father.prototype.say = function() { return "my name is " + this.name; } var Child = function(name) { this.name = name; } Child.prototype = new Father(); var child = new Child('Nico'); child.say();//my name is Nico
可是在上面原型继承的状况,咱们还要对Child的构造函数的constructor作一个声明prototype
Child.prototype.constructor = Child;
由于Child原型是Father实例的一个引用,当想修改Child原型的方法时,会被挂在Father的实例对象上。code
ES6的状况:对象
class Father { constructor(name) { this.name = name; } say() { return "my name is " + this.name; } } class Child extends Father { constructor(name) { super(name) } } var child = new Child('Nico'); child.say()//my name is Nico
es6的class类中,Child类的原型构造器不用作额外声明,由于,es6的class的constructor指向自身继承
子类能使用父类的方法
除了上面说起到的原型链继承,还有例如构造函数继承:ip
function Veticle(name) { this.name = name || null this.drive = function() { console.log(this.name + " drives! --from Veticle "); } } function Car(name) { Veticle.call(this) this.name = name; } var car = new Car('a car'); car.drive();//a car drives! --from Veticle
这种方法核心就经过改变构造函数的上下文(context),达到子类可以使用父类方法,可是这种方法不能使用父类原型方法。
除此以外,还能够遍历父类实例继承父类方法:
function Veticle(name) { this.name = name || null this.drive = function() { console.log(this.name + "drives! --from Veticle "); } } Veticle.prototype.getName = function() { return this.name; } function Car(name) { var veticle = new Veticle(); for (var p in veticle) { console.log(p) Car.prototype[p] = veticle[p]; } this.name = name; } var car = new Car('a car'); car.getName();//a car
这种方法能够获取实例能调用的全部方法,除了不可枚举(ES6 class方法是不可枚举的)
其实关于继承js已经有个很好的的实现方法叫寄生组合继承,大概原理是用构造函数继承实例属性,用原型继承原型方法,而寄生组合继承是在组合继承的基础上强化,二者的区别是前者避免了两次实例化父类,是目前比较好的es5实现继承的方法:
function Veticle(name) { this.name = name || null this.drive = function() { console.log(this.name + "drives! --from Veticle "); } } Veticle.prototype.getName = function() { return this.name; } function Car(name) { Veticle.call(this) this.name = name || 'car' } function inheritProto(subType, superType) { var prototype = Object.create(superType.prototype); subType.prototype.constructor = subType; subType.prototype = prototype; } inheritProto(Car, Veticle) var car = new Car('siip');
js中有一些构造方法诸如Object、Function、String、Number等等,当当前对象调用方法或获取属性时,会顺着自身对象到构造函数原型,再到Object原型,最后到Null这么一条原型链上查找。原型链上有两个关键词prototype和constructor比较重要,prototype是设置构造函数的原型对象,constructor是声明原型的构造函数,无论是对象仍是函数,都有一个隐式属性_proto_用来构成一条完整原型链的指向。 继承有继承属性和继承方法,不少时候es5实现继承比es6要稍微简单一点,es6的class的原型方法是不可枚举的,有时候,挂载方法时须要经过Object.getOwnPropertyNames方法获取。