zoukankan      html  css  js  c++  java
  • WEB前端第三十八课——js类和对象,继承,设计模式

    1.基础

      类,是一种抽象的,并不实际存在的,表示一种事物共有特征的描述

      对象,是具体的,实际存在的,类中的某一个体

      JavaScript是一个典型的“面向过程思想”的语言,语言中不存在类和对象的概念,

      但是,由于JavaScript经常要解决“面向对象思想”的问题,所以JavaScript使用一些方法模拟面向对象思想的特征。

     2.类的创建

      构造函数,通过 new命令生成对象的函数称为构造函数,构造函数一般首字母大写

      new命令的作用是先创建一个对象,然后让对象调用构造函数,因此,构造函数中的 this指向的是 new创建的对象

      语法示例:

        function Student(name, age, sex){

          this .name=name;

          this .age=age;

          this .sex=sex;

        };

        var Lili = new Student('Lili', 20, 'female');

      上述代码中,函数 Student代表类名,通过传递实参向类的属性赋值,new命令将这些键值对构造为对象,然后赋值给变量

      注意,这种方法只是 js中众多创建类的方式之一,且并不是最优的创建方式

    3.面向对象思想

      面向对象(oop),创建一个对象,让对象拥有做某件事的能力(给对象属性和方法),然后命令对象做某件事(封装、继承、多态)

      面向过程(pop),分析出解决问题的所有步骤,将其构建为一个一个函数,然后将这些步骤按照一定顺序实现(顺序、选择、循环)

      oop的核心就是如何构建一个对象,也就是“对象的封装”!

    4.封装

      是指构造具有某种特征的类,以通过对其进行实例化,来获得满足需求的对象的过程

      封装的特征:

        公有,对象中的属性和方法,在对象外部能够直接访问的,称为公有属性和方法

        私有,对象中的属性和方法,仅在对象内部才可以使用的,称为私有属性和方法

      在构造函数中,通过“this .属性”的方式为类添加公有的属性和方法

        this.property所添加的内容在对象外部能够直接被访问

      在构造函数中,添加局部变量和闭包的方式为类添加私有的属性和方法

        局部变量保证了对象外部无法直接获取,闭包保证了对象外部可以间接获取

      对于所有对象都具有的共同属性,为了减少在构造对象时重复传递相同属性值,通过 prototype属性进行统一定义

        语法:类名 .prototype .相同属性值的属性 = 属性值;

    5.原型

      js中给函数提供了一个对象类型的属性,叫作 prototype(原型)

      原型归函数所有,不用创建,是默认就存在的

        语法:Class .prototype .property = ' value ';

      注意,js中提供了一种机制,如果是通过类创建的对象,当访问的属性在对象中没有找到时,

         会去找创建这个对象的类的原型属性,如果能找到,则视为当前对象拥有这个属性。

      本质上,原型的存在就是为了给类的对象添加共同的属性

      作用,使用原型能够有效地节约内存空间,通过原型创建的属性和方法,能够被所有这个类创建的对象访问

    6.构造函数语法小结

      function  ClassName(para1,para2,...) {    //类名首字母须大写
       var privateProperty = 'value';      //定义私有属性
       var privateFunc = function () { }      //定义私有方法
       this.publicProperty1 = para1;      //定义公有属性
       this.publicProperty2 = para2;
        
    this.publicFunc = function(){ };      //定义公有方法(特权函数,可以读写privateProperty)
          ... ...
      }
      ClassName.prototype.publicProperty3 = 'value3';  //定义原型(共有)属性及属性值
      ClassName.prototype.publicFunc=function () { };   //定义原型(共有)方法

       var  obj = new  ClassName(实参1,实参2,…);    //通过类创建对象

    7.原型属性

      结构:原型是一个对象,在原型中通常有两个属性

        ① 构造器Constructor,该属性指向了这个类本身(表明当前原型归属于哪个类所有)

        ② 原型指向_proto_,该属性指向原型本身,提供给通过类创建的对象使用

      作用:创建类的公有属性和公有方法,为创建对象服务

         节约内存空间,不必为每一个对象都分配公有属性和公有方法的内存

      缺点:原型中不能保存数组这类引用类型的数据

         因为地址传递的问题会导致出现修改的连锁变化

         比如,通过 obj .publicProperty .pop(); 删除共有属性数组中的元素,

            会导致其他对象访问 prototype .publicProperty 数组时也缺少了这个元素

    8.原型链

      原型链构成,由对象的“_proto_”属性和对象的构造函数的原型的“_proto_”属性构成的链式结构称为原型链

            原型链的顶端是 Object对象,Object对象没有“_proto_”属性,或者说它的“_proto_”属性指向了自身

      原型链作用,访问对象的属性或方法时,首先在对象本身中查找是否拥有这个属性或方法,

            如果没有找到,那么就沿着原型链逐级向上查找,直到 Object为止

            在任何一级寻找到这个属性或方法都视为对象拥有这个属性或方法(继承)

      原型链创建,函数的原型(prototype) 设置为 另一个函数的对象(实例)

            语法示例:ClassName101 .prototype = new  ClassName1 ;

    9.继承

      在面向对象的语言中,子类能够在不声明的情况下,使用父类的属性和方法的特性叫作继承

      而JavaScript语言本质上并不是一门面向对象的语言,所以,需要通过某种手段来模拟继承,这个方法就是“原型链”

      链式继承示例:

    <script>
        function RichMan() {}
        RichMan.prototype.money='billions of pounds';
        var father=new RichMan();   //创建父类的实例化对象
    
        function Son() {}
        Son.prototype=father;       //创建子类继承关系
        
        var boy=new Son();          //创建子类实例化对象
        console.log(boy.money);     //billions of pounds
    </script>
    

      存在的问题:

        ① 原型链继承,子类实例化不能向父类构造函数传参,但是可以直接访问父类的原型属性

         构造继承,只能访问到父类实例属性,实例化时可以向父类构造函数传参,但是不能访问父类的原型属性和方法

        ② 原型链继承,子类 prototype的 constructor属性,实际上就是父类 prototype的 constructor属性,这样并不合理

         构造函数,子类 prototype的 constructor属性是子类本身,父类也是同样,但构造继承的子类无法享有父类的prototype属性和方法

      构造继承示例:

        function RichMan(fcash,fhouse,fcar) {
            this.cash=fcash;
            this.house=fhouse;
            this.car=fcar;
        }
    /*    RichMan.prototype.money='billions of pounds';
        var father=new RichMan();   //创建父类的实例化对象*/
    
        function Son(scash,shouse,scar) {
            RichMan.call(this,scash,shouse,scar);      //创建子类构造继承关系
        }
        var john=new Son(1,2,3);        //可以访问父类实例属性,but not 原型属性
        var dancy=new Son(4,5,6);
    

    10.组合继承

      为了解决“原型链继承”和“构造继承”的各自缺点,在创建子类继承时同时使用两种继承方式,即组合继承

     1 <script>
     2     function RichMan(fcash,fhouse,fcar) {
     3         this.cash=fcash;
     4         this.house=fhouse;
     5         this.car=fcar;
     6     }
     7     RichMan.prototype.money='billions of pounds';
     8     // var father=new RichMan();   //创建父类的实例化对象
     9 
    10     function Son(scash,shouse,scar) {
    11         RichMan.call(this,scash,shouse,scar);   //创建“构造继承”关系
    12     }
    13     Son.prototype=new RichMan();        //创建“链式继承”关系
    14     var john=new Son(1,2,3);
    15     var dancy=new Son(4,5,6);
    16     console.log(john);                 //Son {cash: 1, house: 2, car: 3}
    17     console.log(dancy.money);          //billions of pounds
    18 
    19 /*    Son.prototype=father;       //创建子类继承关系
    20     var boy=new Son();          //创建子类实例化对象
    21     console.log(boy.money);     //billions of pounds*/
    22 </script>

      组合继承的弊端:

        在子类中调用了两次父类构造函数,一次用于构造继承,一次用于链式继承,也就是对实例属性初始化了两次,

        但这一弊端不太致命,子类在实例化时,构造继承的实例属性覆盖了链式继承的实例属性,只是多消耗了一些内存

      寄生组合继承

        核心思想:通过寄生方式,砍掉父类的实例属性,这样就能在调用两次父类的构造的时候,不会再次实例属性/方法。 

     1 <html lang="en">
     2 <head>
     3     <meta charset="UTF-8">
     4     <title>继承</title>
     5 </head>
     6 <body>
     7 
     8 <script>
     9     function RichMan(fcash,fhouse,fcar) {
    10         this.cash=fcash;
    11         this.house=fhouse;
    12         this.car=fcar;
    13     }
    14     RichMan.prototype.money='billions of pounds';
    15     // var father=new RichMan();   //创建父类的实例化对象
    16 
    17     function Son(scash,shouse,scar) {
    18         RichMan.call(this,scash,shouse,scar);   //创建子类“构造继承”关系
    19     }
    20     // Son.prototype=new RichMan();       //创建“链式继承”关系
    21     Son.prototype.constructor=Son;        //将子类原型属性‘constructor’指向子类本身!
    22 
    23     (function () {                     //创建自执行函数,并嵌套一个空的构造函数 Medi,
    24         function Medi() { }             //将空构造函数插入到原型链作为中间节点,即原子类变为孙类
    25         Medi.prototype=new RichMan();   //这样就避免在每一次子(孙)类实例化时进行两次父类实例化属性和方法
    26         Son.prototype=new Medi();       //也就是用这种寄生方式替代原来的直接“链式继承”关系
    27     }());
    28 
    29     var john=new Son(1,2,3);
    30     var dancy=new Son(4,5,6);
    31     console.log(john);                 //Son {cash: 1, house: 2, car: 3}
    32     console.log(dancy.money);          //billions of pounds
    33 
    34 /*    Son.prototype=father;       //创建子类继承关系
    35     var boy=new Son();          //创建子类实例化对象
    36     console.log(boy.money);     //billions of pounds*/
    37 </script>
    38 </body>
    39 </html>

    11.设计模式

      设计模式(Design Pattern)是一套被反复使用的、多数人知晓的、经过分类的代码设计经验的总结(模板)

      作用:提高代码可重用性、让代码更容易被他人理解、提高代码的可靠性,

         设计模式使代码编写真正工程化,是软件工程的基石脉络,如同大厦的结构一样

      常见种类:

        ① 工厂模式

        ② 构造函数模式

        ③ 原型模式

        ④ 混合模式

        ⑤ 动态原型模式

    12.工厂模式

        <script>
            function RichMan(fcash,fhouse,fcar) {
                var richMan={};             //定义局部变量,类型为Object对象
                richMan.cash=fcash;         //直接定义内部对象的属性
                richMan.house=fhouse;
                richMan.car=fcar;
                return richMan;             //将内部对象作为调用函数的返回值
            }
            var man=RichMan('muchCash','bigHouse','luxuryCar');
            console.log(man);
            console.log(man instanceof RichMan);        //返回值为 false
        </script>
    

      工厂模式是软件开发中经常被使用的一种设计模式

      instanceof 方法一个实例是否归属于一个类。

      通过工厂模式创建的对象,最大的问题是无法确定其属于哪一个类!

    13.构造函数模式

        <script>
            function RichMan(fcash,fhouse,fcar) {
                this.cash=fcash;         //定义this属性
                this.house=fhouse;
                this.car=fcar;
            }
            //通过 new命令创建对象
            var man=new RichMan('muchCash','bigHouse','luxuryCar');
            console.log(man);
            console.log(man instanceof RichMan);        //返回值为 true
        </script>
    

      构造函数和工厂模式最大的区别是:

        没有显示创建一个对象,而是通过 new命令隐式创建一个对象

        然后让隐式对象来实际执行构造函数,因此构造函数中的 this指向的是这个隐式对象

      通过构造函数创建的对象可以明确判断其归属于哪一个类

      构造函数创建对象必须使用 new命令,函数名的首字母通常大写

      构造函数模式最大的问题是面对子类对象共有的属性值,不能有效地节约内存占用!

    14.原型模式

        <script>
            function RichMan(fcash,fhouse,fcar) {}
            //使用 prototype方法定义类的共有属性
            RichMan.prototype.cash='muchCash';
            RichMan.prototype.house='bigHouse';
            RichMan.prototype.car='luxuryCar';
            //通过 new命令创建对象
            var man=new RichMan();
            man.house='manyHouse';  //对于相同属性不同属性值时单独赋值
            console.log(man);
            console.log(man instanceof RichMan);        //返回值为 true
        </script>
    

      弊端:在处理不同属性值的公有属性时,增加了内存的空间占用!

    15.混合模式

        <script>
            function RichMan(fcash,fhouse,fcar) {
                this.cash=fcash; 
                this.house=fhouse;
                this.car=fcar;
            }
            RichMan.prototype.advantage=function () {
                console.log('数钱数到手抽筋')
            }
            //通过 new命令创建对象
            var man=new RichMan('muchCash','bigHouse','luxuryCar');
            console.log(man);
            man.advantage();
        </script>
    

    16.动态原型模式

        <script>
            function RichMan(fcash,fhouse,fcar) {
                this.cash=fcash;
                this.house=fhouse;
                this.car=fcar;
                //使用“懒加载”的方式,定义共有属性的原型
                if (typeof RichMan._initialized=='undefined'){
                    RichMan.prototype.advantage=function () {
                        console.log('数钱数到手抽筋')
                    }
                    RichMan._initialized=true;
                }
            }
            //通过 new命令创建对象
            var man=new RichMan('muchCash','bigHouse','luxuryCar');
            console.log(man);
            man.advantage();
        </script>
    

      懒加载:使用时才加载和占用内存空间,在没有使用之前相当于不存在

      动态原型模式和混合模式很相似,二者都是为了解决原型模式中所有内容都公有的问题

      动态原型模式的特点在于,

        通过判断一个类的 “._initialized” 属性的类型(typeof),进而判断这个类有没有被实例化过,

        如果没有被实例化过,在第一次调用(初始化)时就会将其释放,并将属性值写为 true。

      “._initialized”属性是每一个类都拥有的私有属性,它仅用来表示类是否被实例化过,是Boolean类型的可读写属性。

  • 相关阅读:
    Android:UI界面设计基础知识总结(一)
    Test:河北金力集团企业网集成
    Java中的异常处理try catch(第八周课堂示例总结)
    Java第七周课堂示例总结
    初入JavaWeb(半成品)
    Redis详解(4)--redis底层数据结构
    Go语言流程控制05--defer延时执行
    java==IO练习,文件切割合拼工具开发代码
    【洛谷 P3772】[CTSC2017]游戏(矩阵乘法+线段树)
    【LOJ #2865】「IOI2018」狼人(Kruscal重构树+扫描线)
  • 原文地址:https://www.cnblogs.com/husa/p/13629977.html
Copyright © 2011-2022 走看看