原型模式是js对继承的一种实现javascript
prototype:构造函数中的属性,指向该构造函数的原型对象。java
constructor:原型对象中的属性,指向该原型对象的构造函数函数
_proto_:实例中的属性,指向new这个实例的构造函数的原型对象this
例子:spa
//Person构造函数 function Person() { name = 'Person'; this.height = '160cm'; } //在其原型对象中添加age属性 Person.prototype.age = '18'; //Person的实例p1 var p1 = new Person(); p1.age;//18 p1.name;//undefined p1.height;//160cm
上图中原型与构造函数与实例的关系以下:prototype
** var p1 = new Person()都发生了什么?code
1.var p1 = new Object(); //此时p1._proto_ = Object Prototype对象
2.p1._proto_ = Person.prototype;继承
3.Person.call(p1);//使用新对象p1调用函数Person,将this做用域给p1ip
在定义构造函数的prototype属性的时候,直接吧一个对象赋值给prototype。
例子:
//Person构造函数 function Person(){ name = 'Person'; this.height = '160cm'; } //原型为一个Object实例,并有age属性 Person.prototype = { age: '18' } //Person的实例p1 var p1 = new Person(); p1.age;//18 p1.name;//undefined p1.height;//160cm
上图中原型与构造函数与实例的关系以下:
在实例p1中想要调用一个方法或者属性的时候会沿原型链向上查找。
原型链存在的问题,以下:
//父类构造函数 function Super(){ this.color = ['red','black','blue']; } //子类构造函数 function Sub(){} //子类继承父类 Sub.prototype = new Super(); //新建一个子类的实例 var ins1 = new Sub(); ins1.color.push('green'); var ins2 = new Sub(); //由于color为一个引用对象,ins1和ins2的color都指向同一个地址,修改一个就会修改全部实例的color ins2.color;//'red,belck,blue,green'
防止在原型链模式中,全部子类的实例公用一个父类构造函数的引用对象。
使用 借用构造函数 的方式实现继承。在子类构造函数的内部,调用超类的构造函数构造。以下:
//父类构造函数 function Super(){ this.color = ['red','black','blue']; } //子类构造函数 function Sub(){ //调用父类构造函数实现继承 Super.call(this); } //新建一个子类的实例 var ins1 = new Sub(); ins1.color.push('green'); ins1.color;//'red,belck,blue,green' //在实例ins2上从新执行Super构造函数,从新初始化对象,ins2拥有本身的color属性 var ins2 = new Sub(); ins2.color;//'red,belck,blue'
结合 原型链+借用构造函数 方式,实现组合继承。
借用构造函数使每一个实例拥有本身的属性;原型链使每一个实例能够共用方法,实现方法的复用。
//Super中定义属性name function Super(name){ this.name = name; this.color = ['red','green']; } //Super的原型中定义方法 Super.prototype.sayname = function(){ console.log(this.name); } function Sub(name, age){ //经过构造函数的方式继承Super的属性 Super.call(this, name); //定义本身的属性 this.age = age; } //经过原型链的方式继承方法 Sub.prototype = new Super(); Sub.prototype.constructor = Sub; var ins1 = new Sub('ins1',18);
缺点:会调用两次超类的构造函数,一次在Super.call(this, name); 一次在Sub.prototype = new Super();
致使Sub原型上有属性name、age,Sub实例上也有属性name、age。
借助原型,基于已有的对象建立新对象。
object.Create(参数1,参数2); 参数1用做新对象的原型对象,参数2为新对象定义额外属性的对象。
传入一个参数的状况:
var person = { name: 'person' } //基于已有的person对象,建立一个新的anthorp对象 var anthorp = Object.create(person); //至关于以下语句 function object(o){ function F(){}; F.prototype = o; return new F(); } //获得一个以person为原型的构造函数的实例 var anthorp = object(person);
传入两个参数的状况:
var person = { name: 'person' } //基于已有的person对象,建立一个新的anthorp对象,覆盖以前的name,并新增age var anthorp = Object.create(person,{ name: { value: 'anthorp' }, age: { value: 18 } }); anthorp.name; //anthorp
建立一个用于封装继承过程的函数,在函数内部加强对象。并返回对象。
talk is cheap, show me code。
//建立一个用于封装继承过程的函数,传入obj function create(obj){ //经过调用object方法以obj为基础建立一个新对象。此处object表示任何可以返回新对象的函数 var clone = object(obj); //觉得对象新增方法的方式加强对象 clone.sayHi = function(){ console.log('hi'); }; //返回对象 return clone; } //使用create函数 //一个基础对象person var person = { name: 'person' }; //基于基础对象使用寄生模式生成的新对象 var anthorp = create(person); anthorp.sayHi();
上面讲到组合继承的缺点:会调用两次超类的构造函数。
寄生组合式继承:借用构造函数来继承属性,经过原型链混成来继承方法。没必要在子类原型中调用超类的构造函数。使用寄生继承来继承超类的原型,再将结果制定给子类的原型。
//寄生组合式继承 function inheritProto(Sub, Super){ //根据Super的原型建立一个新的对象proto var proto = object(Super.prototype); //加强新对象,为其赋construtor值 proto.constructor = Sub; //将新对象赋值给子类的原型。 Sub.prototype = proto; } //使用 //Super中定义属性name function Super(name){ this.name = name; this.color = ['red','green']; } //Super的原型中定义方法 Super.prototype.sayname = function(){ console.log(this.name); } function Sub(name, age){ //经过构造函数的方式继承Super的属性,只在此处调用一次Super构造函数 Super.call(this, name); //定义本身的属性 this.age = age; } //调用函数,实现继承。代替以前的Sub.prototype = new Super();语句,防止Super构造函数调用两次 inheritProto(Sub,Super);