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

开头

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

二、对象创建模式

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>
    

    image-20200709194611077

  2. 对象字面量(语法糖

    <body>
        <script>
            var person = {
                name:'chen',
                age:20
            }
            console.log(person);
        </script>
    </body>
    
  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>
    

    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>

image-20200709195914970

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

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

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>

image-20200709200915017

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

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

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>

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. 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>

image-20200709202707272

p1对象叫稳妥对象

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

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>

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>

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>

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>

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>

image-20200709205919489

2.7、总结

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值