【Javascript设计模式】第一课 Javascript中的继承

在Javascript中每个类有三个部分:

    1、第一部分是构造函数内,这是供实例化对象复制用的。

2、第二部分是构造函数外,通过点语法添加的,这是供类使用的,实例化对象是访问不到的。

3、第三部分是类的原型中,实例化对象可以通过其原型链间接访问到,也是为供所有实例化对象所共用的。

一、 子类的原型对象 ——类式继承

类式继承是最常见最简单的继承模式,类式继承用一句话概括就是“父类实例指向子类原型”

  1. /**
  2. * 声明一个父类
  3. * @constructor
  4. */
  5. var SuperClass = function(){
  6. this.superValue = ‘super‘;
  7. }
  8. //为父类添加共有方法
  9. SuperClass.prototype.getSuperValue = function(){
  10. return this.superValue;
  11. }
  12. //声明一个子类
  13. var SubClass = function(){
  14. this.subValue = ‘sub‘;
  15. }
  16. //将父类的实例赋值给子类的原型 子类继承父类
  17. SubClass.prototype = new SuperClass();
  18. //为子类添加共有方法
  19. SubClass.prototype.getSubValue = function(){
  20. return this.subValue;
  21. }

原理概括:类的原型对象的作用是为类的原型添加共有方法,但是类不能直接访问这些属性和方法,必须通过原型prototype来访问,而我们实例化父类时,新创建的对象赋值了构造函数内的属性和方法并且将原型_prototype指向了父类的原型对象,这样就拥有了父类对象原型上的属性和方法,并且这个新创建的对象可以直接访问到父类原型对象上的属性和方法。如果我们将这个新创建的对象赋值给子类的原型,那么子类的原型就可以访问到父类的属性和方法,同时也可以访问从父类构造函数中复制的属性和方法。

测试

  1. var instance = new SubClass();
  2. console.info(instance.getSubValue()); //sub
  3. console.info(instance.getSuperValue());//super

注意:这里说的SubClass继承SuperClass说的是SubClass的原型继承了SuperClass,我们可以通过instanceof来验证一下

  1. console.info(instance instanceof SubClass);//true
  2. console.info(instance instanceof SuperClass);//true
  3. console.info(SubClass instanceof SuperClass); //false
  4. console.info(SubClass.prototype instanceof SuperClass);//true;

不过,这里的类式继承有两个缺点:

第一、由于子类通过prototype来对父类进行实例化,继承了父类,所以说父类中的共有属性要是引用类型,就会在子类中被所有的实例共用,因此一个子类的实例更改子类原型从父类构造函数中继承来的共有属性就会直接影响到其他的子类

  1. var Weaker = function(){
  2. this.books = [‘javascript设计模式‘,‘Java设计模式‘,‘Spring源码深度解析‘];
  3. }
  4. Weaker.prototype.getSuperValue = function(){
  5. console.info(this.books);
  6. }
  7. var WeakerSub = function () {
  8. }
  9. WeakerSub.prototype = new Weaker();

测试如下:

  1. var instance1 = new WeakerSub();
  2. var instance2 = new WeakerSub();
  3. console.info(instance1.books); //["javascript设计模式", "Java设计模式", "Spring源码深度解析"]
  4. instance1.books.push(‘深入浅出Node.js‘);
  5. console.info(instance2.books); // ["javascript设计模式", "Java设计模式", "Spring源码深度解析", "深入浅出Node.js"]

第二、由于子类实现的继承是靠其原型prototype对父类的实例化实现的,因此在创建父类的时候,无法向父类传递参数的,因而在实例化父类的时候也无法对父类构造函数内的属性进行初始化。

二、创建即继承——构造函数继承

构造函数继承中子类的prototype不再指向父类的实例,而是通过在子类中调用父类的call方法来实现继承

  1. /**
  2. * 声明父类
  3. * @param id
  4. */
  5. var constructedSuperClass = function (id) {
    1. //引用类型共有属性
  6. this.books = [‘javascript设计模式‘,‘Java设计模式‘,‘Spring源码深度解析‘];
  7. //值类型共有属性
  8. this.id = id;
  9. }
  10. //父类声明的共有方法
  11. constructedSuperClass.prototype.showBooks = function () {
  12. conole.info(this.books);
  13. }
  14. //声明一个子类
  15. var constructedSubClass = function(){
  16. constructedSuperClass.call(this,id);//call方法更改了函数作用的环境
  17. }

测试:

  1. var instance1 = new constructedSubClass(‘1‘);
  2. var instance2 = new constructedSubClass(‘2‘);
  3. console.info(instance1.books); //["javascript设计模式", "Java设计模式", "Spring源码深度解析"]
  4. console.info(instance1.id); //1
  5. instance1.books.push(‘深入浅出Node.js‘);
  6. console.info(instance2.books); //["javascript设计模式", "Java设计模式", "Spring源码深度解析"]
  7. console.info(instance2.id); //2
  8. console.info(instance1.books); // ["javascript设计模式", "Java设计模式", "Spring源码深度解析", "深入浅出Node.js"]

原理概括:constructedSuperClass.call(this,id)这句话是构造函数继承的精华,由于这个方法可以更改函数的作用的环境,因此在子类中,调用父类的call方法就是将子类中的变量在父类中执行一遍。由于这种继承没有涉及到prototype,所以父类的原型方法自然又不会被子类继承,如果想要被子类继承,就必须放在构造函数内,这样创建出来的实例都会单独拥有一份而不能被共用。

缺点:违背代码复用原则。

三、类式和构造函数的组合——组合继承

类式继承和构造函数继承都有各自的缺点和优点,那么将两者的优点组合在一起,就是所谓的组合继承,即:在子类的构造函数中执行父类的构造函数,在子类的原型上实例化父类

  1. var CombineSuperClass = function (name) {
  2. this.name = name;
  3. this.books = [‘javascript设计模式‘,‘Java设计模式‘,‘Spring源码深度解析‘];
  4. }
  5. CombineSuperClass.prototype.getName = function(){
  6. console.info(this.name);
  7. }
  8. //声明子类
  9. var CombineSubClass = function (name,time) {
  10. //构造函数式继承父类的name属性
  11. CombineSuperClass.call(this,name);
  12. //子类中共有属性
  13. this.time = time;
  14. }
  15. //类式继承 子类原型指向父类实例
  16. CombineSubClass.prototype = new CombineSuperClass();
  17. //子类原型方法
  18. CombineSubClass.prototype.getTime = function () {
  19. console.info(this.time);
  20. }

测试:

  1. var instance1 = new CombineSubClass(‘instance1‘,new Date());
  2. instance1.books.push(‘深入浅出node.js‘);
  3. console.info(instance1.books); //[‘javascript设计模式‘,‘Java设计模式‘,‘Spring源码深度解析‘,‘深入浅出node.js‘];
  4. instance1.getName();//instance1
  5. instance1.getTime();// Date {Fri Sep 30 2016 22:15:15 GMT+0800}
  6. var instance1 = new CombineSubClass(‘instance2‘,new Date());
  7. instance1.books.push(‘Java Web 开发详解‘);//[‘javascript设计模式‘,‘Java设计模式‘,‘Spring源码深度解析‘,‘Java Web 开发详解‘];
  8. console.info(instance1.books);
  9. instance1.getName();//instance2
  10. instance1.getTime();// Date {Fri Sep 30 2016 22:15:15 GMT+0800}

四、洁净的继承者——原型式继承

原型式继承是根据道格拉斯.克罗克福德的《Javascript中原型式继承》得到的。

  1. var inheritObject = function(o){
  2. //声明一个过渡函数对象
  3. function F(){}
  4. //过渡对象的原型继承父级对象
  5. F.prototype = o;
  6. //返回过渡对象的实例,过渡对象的原型继承父级对象
  7. return new F();
  8. }

看结构这是类式继承的一个封装,其中的过渡对象相当于类式继承中的子类,只不过在原型式中作为一个过渡对象存在的,目的是为了创建要返回的新的实例化对象.

测试如下:

  1. var book = {
  2. name: ‘js book‘,
  3. alikebook: [‘css book‘, ‘html book‘]
  4. };
  5. var newbook = inheritObject(book);
  6. newbook.name = ‘ajax book‘;
  7. newbook.alikebook.push(‘jquery book‘);
  8. var otherbook = inheritObject(book);
  9. otherbook.name = ‘bootstrap book‘;
  10. otherbook.alikebook.push(‘node book‘);
  11. console.info(newbook.name);//ajax book
  12. console.info(newbook.alikebook);// ["css book", "html book", "jquery book", "node book"]
  13. console.info(otherbook.name);//bootstrap book
  14. console.info(otherbook.alikebook);// ["css book", "html book", "jquery book", "node book"]

测试结果跟类式继承一样,父类对象中的值类型的属性被复制,引用类型的属性被共享.

五 进一步封装——寄生组合式继承

寄生组合式继承是寄生式继承和构造函数继承的组合,这里处理的不是对象,而是类的原型

  1. var inheritPrototype = function(subClass,superClass){
  2. //赋值一份父类的原型对象保存到变量中
  3. var p = inheritObject(superClass.prototype);
  4. //修正因为重写子类原型导致子类的constructor属性被修改
  5. p.constructor = subClass;
  6. //设置子类的原型
  7. subClass.prototype = p;
  8. }
  1. var SuperClass = function(name){
  2. this.name = name;
  3. this.color = [‘red‘,‘green‘];
  4. }
  5. SuperClass.prototype.getName = function(){
  6. console.info(this.name);
  7. }
  8. var SubClass = function(name,time){
  9. SuperClass.call(this,name);
  10. this.time = time;
  11. }
  12. inheritPrototype(SubClass,SuperClass);
  13. SubClass.prototype.getTime = function () {
  14. console.info(this.time);
  15. }
  16. var instance1 = new SubClass(‘color1‘,‘1‘);
  17. var instance2 = new SubClass(‘color2‘,‘2‘);
  18. instance1.color.push(‘grey‘);
  19. console.info(instance1.color); // ["red", "green", "grey"]
  20. console.info(instance2.color);//[‘red‘,‘green‘]
  21. instance1.getName();//color1
  22. instance2.getName();//color2
  23. instance1.getTime();//1

寄生组合继承的图示如下:

来自为知笔记(Wiz)

时间: 2024-08-28 03:44:18

【Javascript设计模式】第一课 Javascript中的继承的相关文章

《javascript设计模式》读书笔记三(继承)

1.为什么要继承 在设计类的时候,总是在强调减少类与类之间的"耦合度",也就是减少类与类之间的关联.而通过继承能给我们带来什么呢? 1)减少重复性代码 2)弱化对象间的耦合 3)抽象不应该依赖细节:细节应该依赖抽象-<设计模式> 而我们知道像在C#语言中,继承机制操作的话,只需要一个关键字即可,而在javas没有这种机制,还需要模范,下面就是几种模仿继承的方法. 2.原型链式 首先创建一个Person类,其有name属性,然后让一个Author类来继承Person类.效果如

javascript 设计模式 第一章 笔记

1.2若类型语言:  javascript 3种元素类型:布尔值.数值型.字符串.   对象类型和可执行代码的函数类型     null.未定义类型. 原始数据类型按值传递,其他类型按引用传递     数据类型的转换  原始类型可以进行类型转换    1.toString 方法可以把数值.布尔型转换为字符串   2.parsefloat.parseInt函数可以把字符串转换为数值   3.双重"非"可以把字符串.数值转换成布尔值   var bool = !!num; 1.3 函数是一

JavaScript学习第一课

1.innerHTML标签的使用 innerHTML标签相当于嵌入内部网页,可包含类似于<p></p>等标签. 2.img标签的使用 主要有src,example: <!DOCTYPE html> <html> <body> <script> function changeImage() { element=document.getElementById('myimage') if (element.src.match("b

JavaScript 设计模式入门和框架中的实践 http://www.codeceo.com/article/javascript-design-pattern.html

在编写JS代码的过程中,运用一定的设计模式可以让我们的代码更加优雅.灵活. 下面笔者就结合诸如redux的subscribe.ES6的class.vue里面的$dispatch.jquery里面的on/off来给大家简单介绍下设计模式在这些库.语法和框架中的使用. 设计模式解决的问题 设计模式并不是很玄乎的知识,很多同学在编写JS代码的时候已经在不经意间用了不少设计模式了. 笔者认为把设计模式单独抽象出来探讨,就和算法中抽象出来冒泡.排序一样,是为了描述一种常用的JS pattern. 通过研习

Web前端学习-第一课JavaScript篇

Q1:数据类型有哪些? 5种简单数据类型:undefined,null,boolean,number,string: 1种复杂数据类型:object. Undefined只有一个值:undefined: Null只有一个值:null:(表示一个空对象指针) Boolean有两个字面值:true,false:(区分大小写,True和False等都不是boolean值,只是标识符) Number值:整数,浮点数,十进制,八进制(第一位必须是0,严格模式下无效),十六进制(前两位必须是0x),数值范围

javascript设计模式开篇:Javascript 接口的实现

javascript语言不像java. c#. c++等面向对象语言那样有完备的接口支持,在javascript中,接口的实现有三种方式,分别为注释描述.属性检查.鸭式变形.注释描述实现起来最为简单,但是,接口约定的遵守纯靠自觉,而且也没有很好的度量措施,说到底,它主要还是属于程序文档范畴.其实,类是否申明自己支持哪些接口并不重要,只要它具有这些接口中的方法就行了.鸭式变形(这个名称来自James Whitcomb Riley的名言:"像鸭子一样嘎嘎叫的就是鸭子")正式基于这种认识.

javascript设计模式学习之一——javascript面向对象与java的区别

一.静态语言与动态语言面向对象之对比 封装: 1)封装数据:java中通过私有变量Private等实现,javascript中通过函数内的局部变量实现 2)封装实现:对象内部的变化对于外部是透明的,对象之间通过暴露的接口进行通信 3)封装类型:java中通过实现继承(抽象类)或者接口继承(接口)来想方设法实现对象类型的隐藏达到多态的效果,javascript中压根没有这个问题: 4)封装变化:这就是设计模式的目的,封装代码中变化的部分.设计模式分为三种:创建型模式目的在于封装创建对象的变化,结构

设计模式——第一课

设计模式按照特点可以分为三大类型,分别是:创建型.结构型和行为型.常用的设计模式又可以细分为23种. 创建型模式使用来创建对象的模式,抽象了实例化的过程,帮助一个系统独立于其关联对象的创建.组合和表示方式.创建型模式的两个主要功能:1.将系统所使用的具体类的信息封装起来:2.隐藏类的实例是如何被创建和组织的.外界对于这些对象只知道它们有共同的接口,而不清楚具体的实现细节. 常见的创建型设计模式有: 单例模式(Single Pattern):一个类只有一个实例,而且自行实例化并向整个系统提供这个实

【设计模式】Java设计模式第一课之观察者模式

我们可以用一个生活中的情景来认识一下观察者模式. 在这个时代,可能只有我们家里的老年人还保留着每天看报纸的习惯.报社负责生产内容,把它们组织到一张报纸上,每天进行印刷,然后送到每一个花钱订阅了报纸的用户家里.这样老人家就可以浏览一下每天发生的事情了. 这就是观察者模式最贴切的比喻.订阅者和报社是一对多的关系,每天只要更新了内容,就会统一把报纸送到用户的手里,当然,如果你不想订阅了,也可以退订.报社不知道你的状态,也不会关心你到底有没有看报纸,它的职责就是每天准时的把报纸送到你的家里. 下面是我看