欢迎加入QQ讨论群258996829
麦子学院 头像
苹果6袋
6
麦子学院

js继承的6种继承方式

发布时间:2017-03-25 14:45  回复:0  查看:2003   最后回复:2017-03-25 14:45  
本文和大家分享的主要是javascript 继承中常见的 6 种继承方式,一起来看看吧,希望对大家 学习javascript有所帮助。
   类式继承(构造函数)
  JS 中其实是没有类的概念的,所谓的类也是模拟出来的。特别是当我们是用 new  关键字的时候,就使得 的概念就越像其他语言中的类了。类式继承是在函数对象内调用父类的构造函数,使得自身获得父类的方法和属性。 call apply 方法为类式继承提供了支持。通过改变 this 的作用环境,使得子类本身具有父类的各种属性。
  var father = function() {
  this.age = 52;
  this.say = function() {
  alert('hello i am '+ this.name ' and i am '+this.age + 'years old');
  }
  }
  var child = function() {
  this.name = 'bill';
  father.call(this);
  }
  var man = new child();
  man.say();
   原型继承
  原型继承在开发中经常用到。它有别于类继承是因为继承不在对象本身,而在对象的原型上(prototype )。每一个对象都有原型,在浏览器中它体现在一个隐藏的 __proto__ 属性上。在一些现代浏览器中你可以更改它们。比如在 zepto 中,就是通过添加 zepto fn 对象到一个空的数组的 __proto__ 属性上去,从而使得该数组成为一个 zepto 对象并且拥有所有的方法。话说回来,当一个对象需要调用某个方法时,它回去最近的原型上查找该方法,如果没有找到,它会再次往下继续查找。这样逐级查找,一直找到了要找的方法。 这些查找的原型构成了该对象的原型链条。原型最后指向的是 null 。我们说的原型继承,就是将父对像的方法给子类的原型。子类的构造函数中不拥有这些方法和属性。
  var father = function() {
  }
  father.prototype.a = function() {
  }
  var child = function(){}
  // 开始继承
  child.prototype = new father();
  var man = new child();
  man.a();
  可以看到第七行实现了原型继承。很多人并不陌生这种方式。通过在浏览器中打印man 我们就可以查看各个原型的继承关系。
js继承的6种继承方式 
可以看到逐级的关系child->object father 实例化的对象) ->father child 是通过中间层继承了 father 的原型上的东西的。但是为什么中间还有一层 object 呢, 为什么不把child.prototype = father.prototype 答案是如果这样做child father 就没有区别了。大家应该还记得在 prototype 中有个 constructor 属性,指向的是构造函数。按照正常的情况我们要把 constructor 的值改回来指向 child 的构造函数。但如果直接把 father.prototype 赋值给 child.prototype ,那么 constructor 应该指向谁呢?所以很显然只能通过中间层才能使得 child father 保持为独立的对象。
   类式继承和原型继承的对比
  构造函数(类)式继承
  首先,构造函数继承的方法都会存在父对象之中,每一次实例,都会将funciton 保存在内存中,这样的做法毫无以为会带来性能上的问题。
  其次,类式继承是不可变的。无法复用,在运行时,无法修改或者添加新的方法,这种方式是一种固步自封的死方法。实践中很少单纯使用。
  原型继承
  优点:
  原型链可改变:原型链上的父类可替换可扩展
  可以通过改变原型链接而对子类进行修改的。另外就是类式继承不支持多重继承,而对于原型继承来说,你只需要写好extend 对对象进行扩展即可。
  但是原型链继承也有2 个问题。
  第一,包含引用类型值的原型属性会被所有实例共享(可以这样理解:执行sub1.arr.push(2); 先对 sub1 进行属性查找,找遍了实例属性(在本例中没有实例属性),没找到,就开始顺着原型链向上找,拿到了 sub1 的原型对象,一搜身,发现有 arr 属性。于是给 arr 末尾插入了 2 ,所以 sub2.arr 也变了)。
  第二,在创建子类型的实例时,不能向超类型的构造函数中传递参数。(实际上,应该说没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数)实践中很少单纯使用原型链。
  function Super(){this.val = 1;this.arr = [1];
  }function Sub(){// ...
  }
  Sub.prototype = new Super(); //  核心
  var sub1 = new Sub();var sub2 = new Sub();
  sub1.val = 2;
  sub1.arr.push(2);
  alert(sub1.val); // 2
  alert(sub2.val); // 1
  alert(sub1.arr); // 1, 2
  alert(sub2.arr); // 1, 2
  总结:
  类式继承在实例化时,父类可传参,不能复用(父类不可变,每一次实例都会将父类内容保存在内存中)
  原型继承在实例化时,父类不可传参,可以复用(原型链可改变(父类可替换可扩展),父类不会保存在内存中,而是顺着原型链查找,但是结果是原型属性会被所有实例共享(尤其影响引用类型值))
   组合继承(最常用)
  组合继承将原型链和借用构造函数的技术结合到一起,发挥两者之长的一种继承模式。
  思路是使用原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承。
  function SuperType(name){
  this.name = name;
  this.numbers = [1,2,3];
  }
  SuperType.prototype.sayName = function(){
  console.log(this.name);
  }
  function SubType(name,age){
  SuperType.call(this,name);
  this.age = age;
  }
  SubType.prototype = new SuperType();
  SubType.prototype.sayAge = function(){
  console.log(this.age);
  }
  var instance1 = new SubType('aaa',21);
  instance1.numbers.push(666);
  console.log(instance1.numbers);
  instance1.sayName();
  instance1.sayAge();
  var instance2 = new SubType('bbb',22);
  console.log(instance2.numbers);
  instance2.sayName();
  instance2.sayAge();
  把实例函数都放在原型对象上,通过Sub.prototype = new Super(); 继承父类函数,以实现函数复用。
  保留借用构造函数方式的优点,通过Super.call(this); 继承父类的基本属性和引用属性,以实现传参;
  优缺点
  优点:
  1.  可传参
  2.  函数可复用
  3.  不存在引用属性共享问题(图纸)
  缺点:
  1.  (一点小瑕疵)子类原型上有一份多余的父类实例属性,因为父类构造函数被调用了两次,生成了两份,而子类实例上的那一份屏蔽了子类原型上的。。。又是内存浪费,比刚才情况好点,不过确实是瑕疵
来源:csdn
您还未登录,请先登录

热门帖子

最新帖子