干巴爹兔的博客 干巴爹兔的博客
首页
  • 前端文章

    • JavaScript
    • HTML
    • Vue
  • 学习笔记

    • JavaScript教程
    • React学习笔记
    • Electron学习笔记
  • 开源项目

    • cloud-app-admin
    • 下班了吗Vscode插件
    • Subversion变更单插件
  • Server

    • Django
  • 学习笔记

    • MySQL学习笔记
  • 运维

    • 服务器部署
    • Linux
  • 日常学习

    • 学习方法
关于
收藏
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

干巴爹兔

卑微的前端打工人
首页
  • 前端文章

    • JavaScript
    • HTML
    • Vue
  • 学习笔记

    • JavaScript教程
    • React学习笔记
    • Electron学习笔记
  • 开源项目

    • cloud-app-admin
    • 下班了吗Vscode插件
    • Subversion变更单插件
  • Server

    • Django
  • 学习笔记

    • MySQL学习笔记
  • 运维

    • 服务器部署
    • Linux
  • 日常学习

    • 学习方法
关于
收藏
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 理解JS作用域

  • JS面向对象

    • JS面向对象编程笔记(一):原型、原型链
    • JS面向对象编程笔记(二):5种对象创建模式
      • JS面向对象编程笔记(三):5种继承方式
      • JS面向对象编程笔记(完):Object方法与模块化
    • 《JavaScript教程》笔记
    • JS面向对象
    干巴爹兔
    2020-07-09
    目录

    JS面向对象编程笔记(二):5种对象创建模式

    # 开头

    笔记视频内容源自B站JavaScript从入门到放弃 第十二章 面向对象编程 (opens new window),笔记为自行整理复习使用,欢迎一同学习交流,转载请通知原作者

    # 二、对象创建模式

    # 2.1、对象的字面量方式

    1. 对象字面量
    2. 工厂模式
    3. 构造函数模式
      1. 构造函数拓展模式
      2. 寄生构造函数模式
      3. 稳妥构造函数模式
    4. 原型模式
    5. 组合模式
      1. 动态原型模式

    对象字面量:

    1. ​ 通过new构造函数

      <body>
          <script>
              var obj = new Object();
              obj.name = '陈'
              console.log(obj); 
          </script>
      </body>
      
      1
      2
      3
      4
      5
      6
      7

      image-20200709194611077

    2. 对象字面量(语法糖)

      <body>
          <script>
              var person = {
                  name:'chen',
                  age:20
              }
              console.log(person);
          </script>
      </body>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
    3. Object.create(对象):从一个实例对象生成另一个实例对象,create()方法中的参数a作为返回实例对象b的一个原型对象,在a中定义的属性和方法都能被b实例对象继承下来

      <body>
          <script>
            var a = {
              getX: function () {
                console.log("x");
              },
            };
            var b = Object.create(a);
            console.log(b);
            b.getX()
          </script>
        </body>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12

      image-20200709195132818

    # 2.2、工厂模式

    通过字面量创建对象往往在创建多个对象时会产生不必要的冗余,浪费资源,这时候就需要工厂模式:

    <body>
        <script>
    
            function createPerson(name,age){
                var o = new Object();
                o.name = name;
                o.age = age;
                o.sayName = function(){
                    console.log(this.name)
                }
                return o
            }
    
            var p1 = createPerson('chen','18');
            var p2 = createPerson('chen','28');
            var p3 = createPerson('chen','38');
    
            p1.sayName()
        </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    image-20200709195914970

    虽然他解决了之前的问题,但是他自身也有一些问题。它的好处时能创建多个类似的对象,但是没有解决对象识别的问题(都是Object)

    console.log(p1 instanceof Object)
    console.log(p2 instanceof Object)
    
    1
    2

    image-20200709200323610

    如何解决呢,我们就需要使用构造函数模式

    # 2.3、构造函数模式

    创建自定义的构造函数:

    <body>
        <script>
            function Person(name,age){
                this.name = name;
                this.age = age;
                this.sayName = function(){
                    console.log(this.name)
                }
            }
            var man = new Person('chen',18);
            var woman = new Person('chen2',20);
    
            man.sayName()
            woman.sayName()
        </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    image-20200709200915017

    不同于工厂模式,他将变量和方法都存储在了this中,同时解决了工厂函数无法识别对象(即统一为Object)的问题,但是仍会存在一些共同方法同时存在的冗余问题,即man和woman实例中都有一个sayName的方法,但是他们的方法的功能是一样的。但是分别占用了不同的空间,浪费了内存空间。

    console.log(man.sayName === woman.sayName)
    
    1

    image-20200709201219113

    这个就需要构造函数拓展模式来解决

    构造函数拓展模式:

    <body>
        <script>
          function Person(name, age) {
            this.name = name;
            this.age = age;
            this.sayName = sayName;
          }
    
          function sayName() {
            console.log(this.name);
          }
          var man = new Person("chen", 18);
          var woman = new Person("chen2", 20);
    
          console.log(man.sayName === woman.sayName);
        </script>
      </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    image-20200709201434751

    问题:但是定义多个全局函数会严重污染全局空间,没有封装性可言

    寄生构造函数模式:

    创建一个函数,函数体内部实例化一个对象,并且将对象返回,在外部的使用new关键字实例化对象,结合了工厂模式和构造函数模式:

    <body>
        <script>
          function Person(name, age) {
            var o = new Object();
            o.name = name;
            o.age = age;
            o.sayName = function () {
              console.log(this.name);
            };
            return o;
          }
    
          var man = new Person("chen", 18);
          var woman = new Person("chen2", 20);
    
          console.log(man.sayName === woman.sayName);
          console.log(man.__proto__ === Person.prototype);
          console.log(man instanceof Person);
        </script>
      </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    问题:

    1. 定义了相同的方法,浪费了内存空间
    2. instanceof运算符和prototype属性都没有意义

    该模式尽量避免使用

    稳妥构造函数模式:

    没有公共属性,并且它的方法也不引用this对象,不适用new关键字,想要访问其中的私有属性必须通过内部的方法来实现,可以结合闭包理解:

    <body>
        <script>
          function Person(name) {
            var a = 10;
            var o = new Object();
            //name就属于私有属性
            o.sayName = function () {
              console.log(a);
              console.log(name);
            };
            return o;
          }
          var p1 = Person("chen");
          p1.sayName();
        </script>
      </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    image-20200709202707272

    p1对象叫稳妥对象

    console.log(p1.sayName === p2.sayName)
    
    1

    image-20200709203116535

    这种模式也会有浪费内存空间的问题,同时没有使用new操作符构造也会缺失instanceof运算符和prototype属性

    # 2.4、原型模式

    使用prototype属性来定制共享的属性和方法:

    <body>
        <script>
            function Person(){};
    
            Person.prototype.name = 'chen';
            Person.prototype.age = 28;
            Person.prototype.sayName = function(){
                console.log(this.name)
            }
    
            var p1 = new Person()
            p1.sayName()
            console.log(p1)
    
            var p2 = new Person()
            p2.sayName()
    
            console.log(p1.sayName === p2.sayName);
            
        </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    image-20200709203624995

    我们还可以进一步包装:

    <body>
        <script>
          function Person() {}
    
          // Person.prototype.name = 'chen';
          // Person.prototype.age = 28;
          // Person.prototype.sayName = function(){
          //     console.log(this.name)
          // }
    
          Person.prototype = {
            constructor: Person,
            name: "chen",
            age: 28,
            sayName: function () {
              console.log(this.name);
            },
          };
    
          var p1 = new Person();
          p1.sayName();
          console.log(p1);
    
          var p2 = new Person();
          p2.sayName();
    
          console.log(p1.constructor === Person);
    
          console.log(p1.sayName === p2.sayName);
        </script>
      </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

    image-20200709203955592

    但是也会出现一些问题,朋友的状态被共享了:

    <body>
        <script>
          function Person() {}
          Person.prototype = {
            constructor: Person,
            name: "chen",
            age: 28,
            friends: ["a", "b"],
            sayName: function () {
              console.log(this.name);
            },
          };
    
          var me = new Person();
          me.sayName();
    
          var you = new Person();
          you.sayName();
    
          me.friends.push("c");
          console.log(me.friends);
          console.log(you.friends);
        </script>
      </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    image-20200709204248401

    问题:在于引用类型值属性会被所以的实例对象共享,并且会被修改,很少使用原型模式的原因

    # 2.5、组合模式(重要)

    他能解决原型模式状态共享问题,将私有的属性定义在函数内部,共有的方法通过原型去实现继承引用,该模式是目前使用最广泛,认同度最高的一种创建自定义对象的模式:

    <body>
        <script>
          function Person(name, age) {
            this.name = name;
            this.age = age;
            this.friends = ["a", "b"];
          }
    
          Person.prototype = {
              //改变原型对象的同时,要改变该原型对象的constructor属性,让他指向当前的构造函数Person
            constructor: Person,
            sayName: function () {
              console.log(this.name);
            },
          };
    
          var wo = new Person("wo", 18);
          var ni = new Person("ni", 28);
    
    
          console.log(wo);
          console.log(ni);
    
          wo.friends.push('c')
          console.log(wo);
          console.log(ni);
          
          wo.sayName();
          ni.sayName();
          
        </script>
      </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    image-20200709205027640

    组合模式定制当前对象的属性,原型的作用是定制各个实例对象的共享属性

    # 2.6、动态原型模式

    动态原型模式是将组合模式中分开的部分都封装到构造函数中,避免未调用就初始化的问题:

    <body>
        <script>
          function Person(name, age) {
            this.name = name;
            this.age = age;
            this.friends = ["a", "b"];
    
            if (typeof this.sayName != "function") {
              //初始化原型对象上的属性
              Person.prototype.sayName = function () {
                console.log(this.name);
              };
            }
          }
    
          //未被初始化
          console.log(Person.prototype);
    
          var wo = new Person("wo", 18);
          //初始化后
          console.log(Person.prototype);
          console.log(wo instanceof Person)
        </script>
      </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    image-20200709205919489

    # 2.7、总结

    1、字面量方式 **问题:**创建多个对象会造成冗余的代码 2、工厂模式 解决对象字面量方式创建对象的问题 **问题:**对象识别的问题 3、构造函数模式 解决工厂模式的问题 **问题:**方法重复被创建 4、原型模式 解决构造函数模式创建对象的问题 **特点:**在于方法可以被共享 **问题:**给当前实例定制的引用类型的属性会被所有的实例所共享 5、组合模式(构造函数和原型模式) **构造函数模式:**定义实例属性 **原型模式:**用于定义方法和共享的属性,还支持向构造函数中传递参数。 该模式是使用最广泛,应用度最高的一种模式

    了解: 构造函数扩展模式 寄生构造函数模式 稳妥构造函数模式 动态原型模式

    编辑 (opens new window)
    #JavaScript
    上次更新: 2022/08/26, 15:52:02
    JS面向对象编程笔记(一):原型、原型链
    JS面向对象编程笔记(三):5种继承方式

    ← JS面向对象编程笔记(一):原型、原型链 JS面向对象编程笔记(三):5种继承方式→

    最近更新
    01
    使用Vscode开发一个小插件
    10-21
    02
    Vscode插件配置项监听
    10-18
    03
    使用has属性构造必填效果
    10-14
    更多文章>
    Theme by Vdoing | Copyright © 2020-2023 互联网ICP备案: 闽ICP备18027236号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式