1.继承
继承分为:接口继承和实现继承两种。
接口继承:只继承方法的签名;
实现继承:继承实际的方法;(ECMAScript只支持实现继承,只要依靠原型链)
2.继承方式
分为:原型链(主要的继承方法),借用构造函数,组合继承(最常用的继承模式),原型式继承,寄生式继承,寄生组合式继承。
2.1 原型链
概念:子构造函数的原型是父构造函数的的实例,子构造函数的原型指针指向父构造函数的实例,父构造函数的实例的原型指针指向父构造函数原型指针所指向的原型对象,以此类推,层层递进,就构成的实例与原型的链条,这就是原型链。
/** * 原型链 * @constructor */ function Parent() { console.log('调用父类的构造函数'); this.par_name = '我是父类'; } Parent.prototype.getName_parent = function () { console.log('调用父类中的方法----'+this.par_name); }; function Child() { console.log('调用子类的构造函数'); this.chi_name = '我是子类'; } //继承父类 Child.prototype = new Parent(); //子类添加的方法或重写父类发方法必须放在替换原型语句的后面 Child.prototype.getName_child = function () { console.log('调用了子类中的方法----'+this.chi_name); }; var c = new Child(); console.log(c); c.getName_parent(); //调用父类中的方法----我是父类 c.getName_child(); //调用了子类中的方法----我是子类
原型链继承的问题:
(1)、原型对象中的属性共享
(2)、不能向父类构造函数传参
2.2 借用构造函数(伪造对象或经典继承)
用call()和apply()来实现。
/** * 借用构造函数 * @param name * @constructor */ function Parent(name) { this.name = name; this.getName =function () { console.log(this.name); } } function Child(name) { Parent.call(this,name); //继承了Parent,并传递了参数 } var c = new Child('zhangsan'); console.log(c.name); //zhangsan c.getName(); //zhangsan
借用构造函数问题:定义是所有的属性和方法都在构造函数里,不满足函数复用;
2.3 组合继承(伪经典继承)
将原型链和借用构造函数的技术合到一块。思路是使用原型链实现对原型属性和方法的继承,而借用构造函数来实现实例属性的继承。
/** * 组合继承 * @param name * @constructor */ function Parent(name) { console.log('父构造调用'); this.name = name; } Parent.prototype.getName = function () { console.log(this.name); }; function Child(name) { Parent.call(this,name); //继承了Parent,并传递了参数 } Child.prototype = new Parent(); //原型链 var c = new Child('zhangsan'); console.log(c.name); //zhangsan c.getName(); //zhangsan
组合继承的问题:父构造函数需要实例两次;
2.4 原型式继承
在一个函数内建一个空函数,然后将传入该函数的对象作为空函数的原型对象,最后返回一个对象;
用Object.create()来实现,两个参数,第一个作为新对象原型的对象,第二个是为新对象定义额外属性的对象;
/** * 原型式继承 * @type {{name: string, friends: [string]}} */ var person = { name : 'zhansan', friends : ['lisi'] }; function object(person) { function f() {}; f.prototype = person; return new f(); } var per_1 = object(person); var per_2 = object(person); per_1.name = 'zhangsan'; per_2.name = 'wangwu'; per_1.friends.push('wangwu'); per_2.friends.push('zhansan'); console.log(per_1.name); //zhangsan console.log(per_1.friends); // ["lisi", "wangwu", "zhansan"] console.log(per_2.name); //wangwu console.log(per_2.friends); // ["lisi", "wangwu", "zhansan"] //应用Object.create()来实现 var per_3 = Object.create(person,{ id : { value : '123' } }); var per_4 = Object.create(person,{ id : { value : '3665' } }); console.log(per_3.id); //123 console.log(per_4.id); //3665
2.5 寄生式继承
创建一个仅用于封装继承过程的函数,该函数的内部以某种方式来增强对象,最后返回该对象;
/** * 寄生式继承 * @type {{name: string, friends: [string]}} */ var person = { name : 'zhansan', friends : ['lisi'] }; function object(person) { function f() {}; f.prototype = person; return new f(); } function createAnother(obj) { var clone = object(obj); //通过调用函数创建一个新对象 clone.getName = function () { //以某种方式来增强这个对象 console.log(this.name); }; return clone; //将这个对象返回 } var per = createAnother(person); per.getName(); //zhansan
问题:不能做到函数的复用
2.6 寄生组合式继承
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法;
思路:不必为子类型的原型而调用父类的构造函数,使用寄生式来继承父类发原型,然后将结果指定给子类的原型。
/** * 寄生组合式继承 * @param name * @constructor */ function Parent(name) { console.log('父构造调用'); this.name = name; } Parent.prototype.getName = function () { console.log(this.name); }; function Child(name) { Parent.call(this,name); //继承了Parent,并传递了参数 } function object(person) { function f() {}; f.prototype = person; return new f(); } function inheritPrototype(per,chi) { var prototype = object(per.prototype); //通过调用函数创建一个新对象 prototype.getName = function () { //以某种方式来增强这个对象 console.log(this.name); }; chi.prototype = prototype; //指定对象 } inheritPrototype(Parent,Child); var per = new Child('zhangsan'); per.getName(); //zhansan