Javascript实现继承的6种方式

一.类式继承

简介:将父类对象的实例赋值给子类的原型,则子类的原型可以访问父类原型上的属性和方法,以及父类构造函数中复制的属性和方法。

//1.类式继承
//声明父类
function SuperClass() {
  this.superValue = true;
}
//为父类添加公有方法
SuperClass.prototype.getSuperValue = function () {
  return this.superValue;
};
//声明子类
function SubClass() {
  this.subValue = false;
}
//继承父类
SubClass.prototype = new  SuperClass();//将父类对象赋值给子类原型,子类原型可访问父类原型上的属性和方法--类式继承原理
//子类添加公有方法
SubClass.prototype.getSubValue = function() {
  return this.subValue;
};

//测试
var instance = new SubClass();
console.log(instance.getSuperValue());//t
console.log(instance.getSubValue());//f
//
console.log(instance instanceof SuperClass);//t
console.log(instance instanceof SubClass);//t
console.log(SubClass instanceof SuperClass);//f
console.log(SubClass.prototype instanceof SuperClass);//t
通过instanceof来检测某个对象是否是某个类的实例,或者某个对象是否继承了某个类,这样可以判断对象和类之间的继承关系。instanceof通过判断对象的prototype链来确定这个对象是否是某个类的实例。需要注意的是,inatanceof是判断前面的对象是否是后面类(对象)的实例,并不表示两者的继承。所以console.log(SubCass instanceof SuperClass)会打印出false,而console.log(SubClass.prototype instanceof SuperClass)才会打印出true。

类式继承有两个缺点

1.子类通过其原型prototype对父类实例化,继承了父类。但当父类中的共有属性是引用类型时,会在子类中被所有的实例共用,如此在一个子类实例中更改从父类中继承过来的公有属性时,会影响到其他子类。

function SuperClass() {
  this.book = ['javascript','html','css'];
}
function SubClass() {}
SubClass.prototype = new SuperClass();
var ins1 = new SubClass();
var ins2 = new SubClass();
console.log(ins1.book);
console.log(ins2.book);
ins1.book.push("Node");
console.log("after");
//引用类型在子类中被所有实例共用
console.log(ins1.book);//[ 'javascript', 'html', 'css', 'Node' ]
console.log(ins2.book);//[ 'javascript', 'html', 'css', 'Node' ]

2.由于子类是通过原型prototype实例化父类实现继承的,所以在创建父类的时候,无法向父类传递参数,因而在实例化父类的时候无法对父类构造函数内的属性初始化。


二.构造函数式继承

简介:通过在子类的构造函数中执行一次父类的构造函数实现。

//2.构造函数式继承
//声明父类
function SuperClass(id) {
  this.book = ['javascript','html','css'];//引用类型共有属性
  this.id = id;//值类型公有属性
}
//父类声明原型方法
SuperClass.prototype.showBooks = function() {
  console.log(this.books);
}
//声明子类
function SubClass(id) {
  //继承父类
  SuperClass.call(this,id);
}
//测试
var ins1 = new SubClass(1);
var ins2 = new SubClass(2);
ins1.book.push("Node");
console.log(ins1.id);//1
console.log(ins1.book);//['javascript', 'html', 'css', 'Node']
console.log(ins2.id);//2
console.log(ins2.book);//['javascript', 'html', 'css']

ins1.showBooks();//TypeError: ins1.showBooks is not a function
SuperClass.call(this,id)是构造函数式继承的中心。call方法可以改变函数的作用环境,在子类中调用这个方法就是将子类中的变量在父类中执行,父类中给this绑定属性,因而子类继承了父类的共有属性。

缺点:

这种类型的继承没有涉及原型prototype,所以父类的原型方法不会被子类继承。如想被子类继承就必须放在构造函数中,这样创造的每个实例都会单独拥有一份而不能共用,违背了代码复用原则。

三.组合式继承

简介:综合以上两种模式的优点,在子类原型上实例化父类,在子类构造函数中执行一遍父类的构造函数。这样融合了类式继承和构造式继承的优点,过滤了缺点。

//3.组合式继承
function SuperClass(name) {
  this.name = name;
  this.book = ['javascript','html','css'];
}
SuperClass.prototype.getName = function () {
  console.log(this.name);
};
function SubClass(name,time) {
  //构造函数式继承,继承父类name属性
  SuperClass.call(this,name);
  this.time = time;
}
//类式继承,子类原型继承
SubClass.prototype = new SuperClass();
//子类原型方法
SubClass.prototype.getTime = function () {
  console.log(this.time);
};
//测试
var ins1 = new SubClass('Node',2016);
ins1.book.push("Node");
console.log(ins1.book);
ins1.getName();
ins1.getTime();

var ins2 = new SubClass('React',2015);
console.log(ins2.book);
ins2.getName();
ins2.getTime();
缺点:

父类的构造函数执行了两遍:一次在子类的构造函数中call方法执行一遍,一次在子类原型实例化父类的时候执行一遍。


四.原型式继承

简介:对类式继承的封装,过渡对象相当于子类。

//4.原型式继承
function inheritObject(o) {
  //声明过渡函数对象
  function F() {}
  //过渡对象的原型继承父类
  F.prototype = o;
  return new F();
}
//测试
var book = {
  name : "javascript",
  book : ['js','css']
};
var newbook = inheritObject(book);
newbook.name = "ajax";
newbook.book.push("Node");
var otherbook = inheritObject(book);
otherbook.name = "xml";
otherbook.book.push("React");
console.log(newbook.name);//ajax
console.log(newbook.book);//[ 'js', 'css', 'Node', 'React' ]
console.log(otherbook.name);//xml
console.log(otherbook.book);//[ 'js', 'css', 'Node', 'React' ]
console.log(book.name);//javascript
console.log(book.book);//[ 'js', 'css', 'Node', 'React' ]
缺点:

和类式继承一样,父类对象的引用类型值被共用。

五.寄生式继承

简介:寄生式继承其实是对原型继承的第二次封装,并且在第二次封装的过程中对继承的对象进行了拓展。

//5.寄生式继承
function inheritObject(o) {
  //声明过渡函数对象
  function F() {}
  //过渡对象的原型继承父类
  F.prototype = o;
  return new F();
}
//声明基对象
var book = {
  name : "javascript",
  book : ['js','css']
};
function createBook(obj) {
  //通过原型继承方式创建新对象
  var o = new inheritObject(obj);
  //拓展新对象
  o.getName = function() {
    console.log(name);
  }
  //返回拓展后的新对象
  return o;
}

var newbook = createBook(book);
newbook.name = "ajax";
newbook.book.push("Node");
var otherbook = createBook(book);
otherbook.name = "xml";
otherbook.book.push("React");
console.log(newbook.name);//ajax
console.log(newbook.book);//[ 'js', 'css', 'Node', 'React' ]
console.log(otherbook.name);//xml
console.log(otherbook.book);//[ 'js', 'css', 'Node', 'React' ]
console.log(book.name);//javascript
console.log(book.book);//[ 'js', 'css', 'Node', 'React' ]

六.寄生组合式继承

简介:寄生式继承和构造函数式继承结合使用。

//寄生组合式继承
function inheritObject(o) {
  //声明过渡函数对象
  function F() {}
  //过渡对象的原型继承父类
  F.prototype = o;
  return new F();
}
//寄生式继承 继承原型
function inheritPrototype(subClass,superClass) {
  //复制一份父类的原型副本保存在变量中
  var p = inheritObject(superClass.prototype);
  //修正因为重写子类原型导致子类的constructor属性被修改
  p.constructor = subClass;
  //设置子类的原型
  subClass.prototype = p;
}
function SuperClass(name) {
  this.name = name;
  this.colors = ["red","blue","green"];
}
//定义父类原型方法
SuperClass.prototype.getName = function() {
  console.log(this.name);
}
function SubClass(name,time) {
  SuperClass.call(this,name);
  this.time = time;
}
//寄生式继承父类原型
inheritPrototype(SubClass,SuperClass);
//子类新增原型方法
SubClass.prototype.getTime = function() {
  console.log(this.time);
}
//测试
var ins1 = new SubClass("js",2014);
var ins2 = new SubClass("css",2015);
ins1.colors.push("black");
console.log(ins1.colors);
console.log(ins2.colors);
ins2.getName();
ins2.getTime();


注:本文知识点参考《JavaScript设计模式》张荣铭 著  人民邮电出版社



  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值