继承是面向对象的特性(封装、抽象、继承、多态)之一,JavaScript做为面向对象语言天然拥有继承的特性。若是想要真正理解JavaScript的继承机制,那么应该从JavaScript对象的原型提及。javascript
1 prototypejava
每个对象都有一个原型属性,固然,不一样的浏览器对这个属性包装不同。好比咱们使用 Firefox 或者 Google浏览器就能经过 __proto__ 获取属性指向的实例引用(原型对象)。IE浏览器不能经过以上方法获取,并不能说明这个对象不存在!咱们知道JavaScript中函数也是一个对象,一个Function对象,既然是对象他也有原型对象的引用,他的属性名称就是prototype,换句话:咱们可以经过函数调用prototype的方式获取原型引用。函数又能够称做为类,那么类的实例所拥有的原型引用与函数自己所拥有的原型引用是彻底相同的,这就保证了原型共享,为继承打好了基础。浏览器
总结:函数
1)每一个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含全部实例共享的属性和方法。逻辑上能够这么理解:使用原型的好处可让全部对象实例共享它所包含的属性和方法,并且函数自己与该函数全部的实例共享一个原型。测试
2)函数调用原型采用 Class.prototype,实例调用原型属性 obj.__proto__,判断(Class.prototype ==obj.__proto__)this
3)原型是一个对象,一个实例化的对象,这个对象的实例化过程比较特殊,特殊之处在于他不是经过类自己去实例化的,可是他又是该类的实例。好比一个类 function Person(name,age){ this.name=name; ....}; var p = new Person('learn',23);若是p的原型是经过类自己去实例化的话,那么原型必定会有一个name属性,然而实际上原型是一个 Person{ };却不是一个Person的实例,又不是经过Person的构造方法new出来的。能够用 instanceof 测试这个原型不属于Person实例。spa
function Person(name ,age){ this.name = name; this.age = age; this.info = function(){ return this.name + this.age; }; } var p = new Person('learn',23); console.info(Person.prototype); // Person{} console.info(p.__proto__); // Person{} console.info(Person.prototype == p.__proto__ );//true console.info(p.__proto__ instanceof Person);//false
2 constructorprototype
与原型同样,每个函数都有一个constructor属性,每个函数的原型也有一个constructor属性,既然原型中有constructor属性,天然每个对象实例就能共享这个constructor属性。那么这里要注意了:函数其实有两个constructor属性,一个是自身的属性,能够经过 Class.constructor 获取(js属性调用就近原则),一个是原型中的constructor属性,能够经过 Class.prototype.constructor 或者 obj.__proto__.construcotr 获取。这两个是有本质差别的,前者是一个匿名函数的引用,后者是该类自己的引用。并且前者是类专有私有的属性,是不被实例共享的。实际开发中,咱们不会去触碰前者,咱们每每是在继承的时候对后者进行改变。code
总结:对象
1)每一个函数都有一个类私有的constructor属性和一个原型当中共享的constructor属性
2)只有类自己才能调用这个私有属性:Class.constructor ;原型constructor属性调用:obj.consturctor 或 obj.__proto__.construcotr 或 Class.prototype.constructor
3)类私有的constructor属性指向一个匿名函数的引用,注意是函数引用,不是函数的实例引用;原型的constructor属性则是指向类自己的引用,这里也是函数引用,不是实例引用。
4)原型的constructor属性是一个类的重要标志,他必定要指向类自己,由于面向对象规定构造函数指向自己。然而类的私有constructor属性是由系统决定,咱们不最好不要触碰。
console.info(Person.constructor);//Function() console.info(Person.prototype.constructor);// Person(name, age) console.info(p.constructor);// Person(name, age) console.info(p.__proto__.constructor);// Person(name, age)
3 extend
JavaScript是经过原型进行继承的,原型的做用就是共享,因此经过原型能够很好的达到继承机制。采用原型继承时要注意必不可少的两点:子类的原型必须指向父类的实例,子类原型的constructor属性必须指向子类自己。经过原型指向父类实例,从而全部子类实例共享父类的属性与方法,达到继承效果。经过子类原型的constructor属性指向子类自己,达到面向对象中要求的构造函数指向之间。
总结:
1)子类的原型指向父类的实例,SubClass.prototype = new SuperClass( );
2)子类的原型构造函数指向子类自己,SubClass.prototype.constructor = SubClass ;
3 ) 因为js使用原型继承,致使构造函数也被继承,然而面向对象来讲这是错误的,因此才有第二步从新指向构造函数。
4 ) 我的理解 Person 与 Person.prototype 的差别是:前者是指构造函数或类自己,后者实际上是一个全部实例都共享的实例对象。
5)若是单纯只是调用某一个函数那么可使用 call 函数进行处理,而无需继承。 Class.method.call(Self , param) ;
function extends( SubClass , SuperClass){ /*第一步 : 构建桥梁类Bridge,他的做用就是彻底替代父类自己,包括构造方法*/ var Bridge = function( ){ } ; Bridge.prototype = new SuperClass( ); // Bridge.prototype.constructor = SuperClass ;这一步原型链默认完成 /*第二步 : 使用子类的原型链继承桥梁父类*/ SubClass.prototype = new Bridge( ); SubClass.prototype.constructor = SubClass; /*第三步 : 扩展子类属性,把父类的引用做为子类的共享属性,为子类中所调用 */ SubClass.prototype.superClass = SuperClass.prototype; // 这里必须是prototype,而不能是函数自己 /*第四步 : 为保证程序正常运行机制,作个小判断*/ if( SuperClass.prototype.constructor == Object.prototype.constructor ){ SuperClass.prototype.constructor = SuperClass; } } 6)javascript是单继承的,但若是想在一个类中拥有多个类的方法,那么就要使用聚合(掺元类,把其余类的方法为本身所用)。 具体以下: function mixin(ReceivingClass,GivingClass){ for(var method in GivingClass.prototype ){ if(ReceivingClass.prototype[method] == undefined){ /* 这里特别注意使用prototype而不使用缘由就是 静态属性 与 原型属性的差别 */ ReceivingClass.prototype[method]= GivingClass.prototype[method]; } } }