学习JavaScript设计模式(继承)

1、继承

在javascript中继承是一个非常复杂的话题,比其他任何面向对象语言的中的继承都复杂得多。在大多数其他面向对象语言中,继承一个类只需要使用一个关键字即可。与它们不同,在javascript中要想达到传承公用成员的目的,需要采取一系列措施。更有甚者,javascript属于使用原型式继承的少数语言之一。利益于这种语言的灵活性,你既可使用标准的基于类的继承,也可使用更微妙一些的原型式继承。

2、为什么需要继承?

一般来说,在设计类的时候,我们希望能减少重复性的代码,并且尽量弱化对象间的耦合。使用继承符合前一个设计原则的需要。借助这种机制,你可以在现有类的基础上进行设计并充分利用它们已经具备的各种方法,而对设计进行修改也更为轻松。假设你需要让几个类都拥有一个按特定方式输出类结构的toString()方法,当然可以用复制加粘贴的办法把定义toString()方法的代码添加到每一个类中,但这样做的话,每当需要改变这个方法的工作方式时,你将不得不在每一个类中重复同样的修改。反之,如果你提供了一个ToStringProvider类,然后让那些类继承这个类,那么toString这个方法只需在一个地方声明即可。

让一个类继承另一个类可能会导致二者产生强耦合,也即一个类的依赖于另一个类的内部实现。我们将讨论一些有助于避免这种问题的技术,其中包括用掺元类为其他类提供方法这种技术。

3、基于类的继承

下面看下面的代码:

<script type="text/javascript"> 

  function Person(name, age)
  {
   this.name = name;
   this.age = age;
  }
  Person.prototype.say = function ()
  {
   console.log(this.name + " , " + this.age);
  }
  function Student(no)
  {
   this.no = no;
  }
  /**
   * Student的prototype指向Person的对象
   */</span>
  Student.prototype = new Person();
  var stu1 = new Student("0001");
  stu1.name = '张三';
  stu1.age = '11';
  console.log(stu1.no);
  stu1.say();
 </script>

输出结果:  
张三 , 11  
可以看到Student成功集成了Person,并且拥有了Person的say方法,核心代码其实就是一句 Student.prototype = new Person();,下面通过图解来说明原理:

将Student.prototype指向new Person() , new Person的_proto_又指向Person Prototype;这样完成了整个继承。

但是这种方式存在问题:

问题1:当父类存在引用类型变量时,造成数据不一致,下面我们给Person添加一个hobbies属性,类型为数组。

<script type="text/javascript">
  /**
  * 存在问题
  * 1、无法在Student的构造方法中传递参数用于父类的构造方法
  * 2、对于引用类型变量,造成数据不一致
  */ 

  function Person(name, age)
  {
   this.name = name;
   this.age = age;
   this.hobbies = [] ;
  }
  Person.prototype.say = function ()
  {
   console.log(this.name + " , " + this.age +" , " +this.hobbies);
  }
  function Student(no)
  {
   this.no = no;
  }
  Student.prototype = new Person(); 

  var stu1 = new Student("0001");
  stu1.name = '张三';
  stu1.age = '11';
  stu1.hobbies.push("soccer");
  stu1.say(); 

  var stu2 = new Student("0002");
  stu2.name = '李四';
  stu2.age = '12';
  stu2.hobbies.push("girl");
  stu2.say();
 </script>

输出结果:
张三 , 11 , soccer  
李四 , 12 , soccer,girl  
可以看出,李四的hobbies应该只有girl,但是上面的代码让所有对象共享了hobbies属性。
上述的继承方式还存在一个问题:

问题2:在Student的构造方法中,无法使用new Student(“00001” , “张三” , 12) ;创建对象,并初始化name和age属性,必须stu.name, stu.age进行赋值

为了解决上述问题,对上述代码进行修改:

<script type="text/javascript"> 

  function Person(name, age)
  {
   this.name = name;
   this.age = age;
   this.hobbies = [];
  }
  Person.prototype.say = function ()
  {
   console.log(this.name + " , " + this.age +" , " + this.hobbies);
  } 

  function Student(name, age, no)
  {
   /**
   * 使用call方法,第一个参数为上下文;
   * 有点类似Java中的super(name,age)的感觉
   */
   Person.call(this, name, age);
   this.no = no;
  } 

  Student.prototype = new Person(); 

  var stu1 = new Student("0001","张三",11);
  stu1.hobbies.push("soccer");
  stu1.say(); 

  var stu2 = new Student("0002","李四",12);
  stu2.hobbies.push("cangjin");
  stu2.hobbies.push("basketball");
  stu2.say();
 </script>

输出:
0001 , 张三 , soccer  
0002 , 李四 , cangjin,basketball  
在Student的构造方法中使用了Person.call(this,name,age)感觉就像super(name,age)【call的第一个参数为上下文】;并且成功解决了对引用属性的共享问题,完美解决。

4、基于原型链的继承

<script type="text/javascript"> 

 /**
  * 基于原型链的集成中都是对象
  * 存在问题:
  * 1、对于引用类型变量,造成数据不一致
  */
 var Person = {
    name: "人",
    age: 0,
    hobbies: [],
    say: function ()
    {
     console.log(this.name + " , " + this.age + " , " + this.hobbies);
    }
   }
   ; 

 var Student = clone(Person);
 Student.no ="";
 Student.sayHello = function()
 {
  console.log(this.name +"hello ") ;
 } 

 var stu1 = clone(Student);
 stu1.name = "zhangsan";
 stu1.age = 12;
 stu1.hobbies.push("Java");
 stu1.say(); 

 var stu2 = clone(Student);
 stu2.name = "lisi";
 stu2.age = 13;
 stu2.hobbies.push("Javascript");
 stu2.say(); 

 /**
  * 返回一个prototype执行obj的一个对象
  * @param obj
  * @returns {F}
  */
 function clone(obj)
 {
  var F = function ()
  {
  };
  F.prototype = obj;
  return new F(); 

 }
</script>

输出:
zhangsan , 12 , Java  
lisi , 13 , Java,Javascript  
可以看出同样存在引用属性不一致的问题,并且整个操作全部基于对象,给人的感觉不是很好,下面通过图解解释下原理:

对象间通过一个clone函数,不断的返回一个新的对象,且prototype执行传入的对象,整个继承过程其实就是_proto_不断的指向,形成一个链,所以叫做原型链。

好了,已经介绍完了,js的两种集成的方式,最好使用的还是通过类的继承,比较稳定。

以上就是关于继承知识点的相关内容介绍,希望对大家的学习有所帮助。

(0)

相关推荐

  • JavaScript继承基础讲解(原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承)

    说好的讲解JavaScript继承,可是迟迟到现在讲解.废话不多说,直接进入正题. 既然你想了解继承,证明你对JavaScript面向对象已经有一定的了解,如还有什么不理解的可以参考<面向对象JS基础讲解,工厂模式.构造函数模式.原型模式.混合模式.动态原型模式>,接下来讲一般通过那些方法完成JavaScript的继承. 原型链 JavaScript中实现继承最简单的方式就是使用原型链,将子类型的原型指向父类型的实例即可,即"子类型.prototype = new 父类型();&qu

  • functional继承模式 摘自javascript:the good parts

    这种模式的作用在于确保对象属性的真正私有化, 我们无法直接访问对象的状态, 只能通过特权方法来进行操作. 下面直接来个例子吧. 复制代码 代码如下: var person = function(cfg) { var that = {}; that.getName = function() { return cfg.name || 'unknow name'; }; // 性别 默认男性 that.getGender = function() { return cfg.gender || 'mal

  • JavaScript继承模式粗探

    真正意义上来说Javascript并不是一门面向对象的语言,没有提供传统的继承方式,但是它提供了一种原型继承的方式,利用自身提供的原型属性来实现继承.Javascript原型继承是一个被说烂掉了的话题,但是自己对于这个问题一直没有彻底理解,今天花了点时间又看了一遍<Javascript模式>中关于原型实现继承的几种方法,下面来谈谈JS中比较简单的继承方法,如果大家有不同意见,欢迎建议. 最基础的原型链继承在这里就不复述了,主要讲一下其他的继承模式. 1.借用构造函数继承 function Fa

  • javascript继承的六大模式小结

    1.原型链 function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //继承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubV

  • javascript中最常用的继承模式 组合继承

    复制代码 代码如下: <script type="text/javascript"> //创建基类 function Person(name, age) { this.name = name; this.age = age; } //通过原型方式给基类添加函数(这样可以服用此函数) Person.prototype.showName = function () { alert(this.name); } //创建子类 function Student(name, age,

  • JavaScript设计模式学习之“类式继承”

    在做一件事情之前,首先要清楚做这件事情的好处有什么,相信不会有哪个人愿意无缘无故的去做事情.一般说来,我们在设计类的时候,实际上就是希望能减少重复性的代码,使用继承可以完美的做到这一点,借助继承机制,你可以在现有类的基础上再次进行设计并且充分利用它们已经具备的各种方法,而对设计的修改也更为轻松.废话不多说了,举例说明: 复制代码 代码如下: function Person(name){     this.name = name; } Person.prototype.getname = func

  • 学习JavaScript设计模式(继承)

    1.继承 在javascript中继承是一个非常复杂的话题,比其他任何面向对象语言的中的继承都复杂得多.在大多数其他面向对象语言中,继承一个类只需要使用一个关键字即可.与它们不同,在javascript中要想达到传承公用成员的目的,需要采取一系列措施.更有甚者,javascript属于使用原型式继承的少数语言之一.利益于这种语言的灵活性,你既可使用标准的基于类的继承,也可使用更微妙一些的原型式继承. 2.为什么需要继承? 一般来说,在设计类的时候,我们希望能减少重复性的代码,并且尽量弱化对象间的

  • 学习JavaScript设计模式之装饰者模式

    有时我们不希望某个类天生就非常庞大,一次性包含许多职责.那么我们就可以使用装饰着模式. 装饰着模式可以动态地给某个对象添加一些额外的职责,从而不影响这个类中派生的其他对象. 装饰着模式将一个对象嵌入另一个对象之中,实际上相当于这个对象被另一个对象包装起来,形成一条包装链. 一.不改动原函数的情况下,给该函数添加些额外的功能 1. 保存原引用 window.onload = function() { console.log(1); }; var _onload = window.onload ||

  • 学习JavaScript设计模式之责任链模式

    一.定义 责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 二.示例 假设这么一个场景: 我们负责一个售卖手机的电商网站,经过分别缴纳500元定金和200元定金的两轮预定后,到了正式购买阶段.针对预定用户实行优惠,支付过500元定金的用户会收到100元的商城优惠券,支付过200元定金的用户会收到50元的商城优惠券,没有支付定金的用户归为普通购买,且在库存有限的情况下不一定保证买到. /*

  • 学习JavaScript设计模式之模板方法模式

    一.定义 模板方法是基于继承的设计模式,可以很好的提高系统的扩展性. java中的抽象父类.子类 模板方法有两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类. 二.示例 Coffee or Tea (1) 把水煮沸 (2) 用沸水浸泡茶叶 (3) 把茶水倒进杯子 (4) 加柠檬 /* 抽象父类:饮料 */ var Beverage = function(){}; // (1) 把水煮沸 Beverage.prototype.boilWater = function() { conso

  • 学习JavaScript设计模式(接口)

    1.接口概述 1).什么是接口? 接口是提供了一种用以说明一个对象应该具有哪些方法的手段.尽管它可以表明这些方法的语义,但它并不规定这些方法应该如何实现. 2). 接口之利 促进代码的重用. 接口可以告诉程序员一个类实现了哪些方法,从而帮助其使用这个类. 有助于稳定不同类之前的通信方式. 测试和调式因此也能变得更轻松. 在javascript这种弱类型语言中,类型不匹配错误很难跟踪.使用接口可以让这种错误的查找变午更容易一点,因为此时如果一个对象不像所要求的类型,或者没有实现必要的方法,那么你会

  • 学习JavaScript设计模式之策略模式

    把不变的部分和变化的部分隔开是每个设计模式的主题. 条条大路通罗马.我们经常会遇到解决一件事情有多种方案,比如压缩文件,我们可以使用zip算法.也可以使用gzip算法.其灵活多样,我们可以采用策略模式解决. 一.定义 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 基于策略类模式的程序至少由两部分组成.第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程.第二个部分是环境类Context,Context接收客户的请求,随后把请求委托给某一个策略类. 二.示例 计

  • 学习JavaScript设计模式之迭代器模式

    迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. JavaScript中的Array.prototype.forEach 一.jQuery中的迭代器 $.each([1, 2, 3], function(i, n) { console.log("当前下标为:"+ i + " 当前元素为:"+ n ); }); 二.实现自己的迭代器 var each = function(ary, callback) { for(var i

  • 学习JavaScript设计模式之状态模式

    状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变. 当电灯开着,此时按下开关,电灯会切换到关闭状态:再按一次开关,电灯又将被打开.同一个开关在不同的状态下,表现出来的行为是不一样的. 一.有限状态机 状态总数(state)是有限的. 任一时刻,只处在一种状态之中. 某种条件下,会从一种状态转变(transition)到另一种状态. 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类. 解释: (1)将状态封装成独立的类,并将请求委托给当前的状态对

  • 学习JavaScript设计模式之中介者模式

    一.定义 面向对象设计鼓励将行为分布到各个对象中,把对象划分成更小的粒度,有助于增强对象的可复用性.但由于这些细粒度对象之间的联系激增,又可能反过来降低它们的可复用性. 中介者模式的作用就是解除对象与对象之间的紧耦合关系. 二.示例:购买商品 假设我们正在开发一个购买手机的页面,购买流程中,可以选择手机颜色以及输入购买数量,同时页面中可以对应展示输入内容.还有一个按钮动态显示下一步操作(该颜色库存量充足,显示下一步:否则显示库存不足). <div> <span>请选择颜色</

  • 学习JavaScript设计模式之观察者模式

    一.定义 观察者模式(发布-订阅模式):其定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 在JavaScript中,一般使用事件模型来替代传统的观察者模式. 好处: (1)可广泛应用于异步编程中,是一种替代传递回调函数的方案. (2)可取代对象之间硬编码的通知机制,一个对象不用再显示地调用另外一个对象的某个接口.两对象轻松解耦. 二.DOM事件–观察者模式典例 需要监控用户点击document.body的动作,但是我们没有办法预知用户将在什么时间点击

随机推荐