一.通过构造继承
/* 通过构造函数实现继承 */
// 声明父类
function Parent (name) {
this._name = name;
this.hobby = ['读书', '听歌', '打羽毛球'];
}
// 声明父类原型方法
Parent.prototype.getName = function () {
return this._name;
}
// 声明子类
function Child (name, age){
Parent.call(this, name); //这里是实现构造函数继承的精华
this._age = age;
}
// 为子类添加原型方法
Child.prototype.getAge = function () {
return this._age;
}
队长点评:
通过构造函数实现的继承,没有涉及到原型prototype,所以父类的原型方法自然不会被子类继承。而想要被子类继承就必须放在构造函数中,这样创建出来的每个实例都会单拥有一份而不能共用,这样就违背了代码复用的原则。
二.通过原型继承
/* 通过原型继承 */
// 声明父类
function Parent( name ){
this._name = name;
this.hobby = ['读书', '听歌', '打羽毛球'];
}
// 声明父类原型方法
Parent.prototype.getName = function(){
return this._name;
}
// 声明子类
function Child( sex ){
this._sex = sex;
}
// 继承父类
Child.prototype = new Parent();
// 为子类添加原型方法
Child.prototype.getSex = function(){
return this._sex;
}
var instance1 = new Child();
var instance2 = new Child();
console.log( instance2.hobby ); //['读书', '听歌', '打羽毛球']
instance1.hobby.push('跑步');
console.log( instance2.hobby ); //["读书", "听歌", "打羽毛球", "跑步"]
/*
将父类的实例赋予子类的原型prototype,父类创建的实例对象
不仅可以访问父类原型上的属性与方法,同样也可以访问从父类
构造函数中复制的属性和方法,将这个对象赋值给子类的原型,则
可以访问父类的原型属性与方法的同时,还可以访问父类构造函
数中复制的属性与方法.这是原型继承原理.
*/
队长点评:
1.由于子类通过其原型prototype对父类的实例化,而继承了父类;如果父类中的共有属性要是引用类型,就会在子类中被所有实例共用,因此一个子类的实例更改子类原型从父类构造函数中继承来的共有属性就会直接影响到其它子类。
2.由于子类实现的继承是靠其原型prototype对父类的实例化实现的,因此在创建父类的时候是无法向父类传递参数的,因而在实例化父类的时候也无法对父类构造函数内的属性进行初始化。
三.组合继承
/* 组合式继承 */
// 声明父类
function Parent( name ){
this._name = name;
this.hobby = ['读书', '听歌', '打羽毛球'];
}
// 声明父类原型方法
Parent.prototype.getName = function(){
return this._name;
}
// 声明子类
function Child( name, sex ){
//构造函数继承父类name属性
Parent.call( this, name );
// 子类中新增的共有属性
this._sex = sex;
}
// 通过原型继承,子类继承父类
Child.prototype = new Parent();
// 为子类添加原型方法
Child.prototype.getSex = function(){
return this._sex;
}
var instance1 = new Child();
var instance2 = new Child();
console.log( instance2.hobby ); //['读书', '听歌', '打羽毛球']
instance1.hobby.push('跑步');
console.log( instance2.hobby ); //['读书', '听歌', '打羽毛球']
console.log( instance1.__proto__.constructor === Child); //false
console.log( instance1.__proto__.constructor === Parent); //true
队长点评:
子类的实例中更改父类继承下来的引用类型属性,不会影响到其它实例,并且子类实例化过程中又能又能将参数传递到父类的构造函数中.
缺点是: 一.父类的构造函数调用了二遍; 二.子类实例的原型指向的构造函数不是指向的子类构造函数而是父类的构造函数.
四.组合继承优化版
/* 组合式继承优化版 */
// 声明父类
function Parent( name ){
this._name = name;
this.hobby = ['读书', '听歌', '打羽毛球'];
}
// 声明父类原型方法
Parent.prototype.getName = function(){
return this._name;
}
// 声明子类
function Child( name, sex ){
//构造函数继承父类name属性
Parent.call( this, name );
// 子类中新增的共有属性
this._sex = sex;
}
// 通过原型继承,子类继承父类
Child.prototype = Object.create( Parent.prototype );
Child.prototype.constructor = Child;
// 为子类添加原型方法
Child.prototype.getSex = function(){
return this._sex;
}
var instance1 = new Child();
var instance2 = new Child();
console.log( instance2.hobby ); //['读书', '听歌', '打羽毛球']
instance1.hobby.push('跑步');
console.log( instance2.hobby ); //['读书', '听歌', '打羽毛球']
console.log( instance1.__proto__.constructor === Child); //true
console.log( instance1.__proto__.constructor === Parent); //false
队长点评:
Object.create( Parent.prototype ); 创建一个中间对象实现连接
Child.prototype.constructor = Child; 改变子类原型的构造函数指向
五.多继承
// 多继承
var mix = function(){
var len = arguments.length, // 获取参数长度
target = arguments[0], // 第一个对象为目标对象
arg; // 缓存参数对象
// 遍历被继承的对象
for( var i=1; i<len; i++ ){
// 缓存当前对象
arg = arguments[i];
// 遍历被继承的对象中的属性
for( var property in arg ){
// 将被继承中的属性复制到目标对象中
target[property] = arg[property];
}
}
// 返回目标对象
return target
}
队长点评:
这是一个浅复制,使用时请注意
六.ES6的继承
class Parent {
constructor(name){
this._name = name;
}
getName(){
return this._name;
}
}
class Child extends Parent {
constructor(name, age){
super(name);
this._age = age;
}
getAge(){
return this._age;
}
}
本文实例来源于张容铭大神写的《JavaScript设计模式》
网友评论