面向对象之其他模式

本文首发于个人博客:www.wyb.plus

1. 组合使用构造函数和原型

首先再来回顾一下这三种模式 : 构造函数模式 , 原型模式 以及构造函数和原型的组合模式

构造函数模式 >>>

function Person(name, age) {
      this.name = name;
      this.age = age;
      this.sayName = function() {
          console.log(this.name);
      }
  }

缺点 : 浪费内存

原型模式 >>>

function Person() {}
Person.prototype = {
 constructor: Person,
 name: 'wangyubo',
 age: 18,
 girlfriends: {
     first: "斋藤飞鸟",
     second: "迪丽热巴"
 },
 sayName: function() {
     console.log(this.sayName);
 }
}

缺点 : 原型的覆盖问题和原型的重写问题 , 不能传递参数

组合模式 >>>

function Person(name, age) {
 this.name = name;
 this.age = age;
 this.girlfriends = {
     first: "斋藤飞鸟",
     second: "迪丽热巴"
 }
}
Person.prototype = {
 constructor: Person,
 sayName: function() {
     console.log(this.sayName);
 }
}
  • 创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。
  • 构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
  • 每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存
  • 另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长

组合使用构造函数和原型时有这么几个注意点:

  • 对于对象内后期需要修改的引用类型的继承的值,这个值以构造函数的this.属性名称的方式来实现继承,这样可以避免某个示例操作继承来的对象而引起其他实例的变化
  • 这种构造函数与原型混成的模式,是目前在ECMAScript 中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式

2. 动态原型模式

动态原型模式把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型

function Person(name, age) {
         this.name = name;
         this.age = age;
         this.girlfriends = {
             first: "斋藤飞鸟",
             second: "迪丽热巴"
         }
         if (typeof this.sayName !== "function") {
             Person.prototype.sayName = function() {
                 console.log(this.name);
             }
         }
     }
let wyb = new Person("xxx", 18)
let wangyubo = new Person("yyy", 28)

此时条件满足 , 原型被修改 , sayName方法被添加到原型上

在这里插入图片描述

使用动态原型模式时,不能使用对象字面量重写原型。

前面已经解释过了,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

3. 寄生构造函数模式

这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数

function Person(name, age) {
      let obj = new Object();
      obj.name = name;
      obj.age = age;
      obj.sayname = function() {
          console.log(this.name);
      }
      return obj
  }
let wyb = Person("xxx", 18)//这样就是工厂模式
let wangyubo = new Person("yyy", 28)//这样就是寄生构造函数模式
//没错 他们的区别就在于是否是使用new操作符

在这里插入图片描述

看起来这两种模式生成的对象没有任何区别 , 都是由Object对象生成的

但是如果我给构造函数不添加返回值 , 那么结果就完全就不同了

//return obj注释掉
//turn obj

在这里插入图片描述

此时工厂模式创造的实例对象wyb为undefined , 寄生构造函数模式创造的实例对象wangyubo为Person构造的对象

这个问题的原因和上面他们没区别的原因都是一样的----return obj 手动截胡

  • Person函数在没有注释掉return之前 , 最终返回值为obj , obj是由 Object()函数构造出来的 , 所以无论使用工厂模式还是寄生模式 , 都受到这个返回值的影响
  • 而在return注释掉以后 , 工厂模式由于没有返回值 , 所以生成的实例对象wyb为undefined
  • 寄生模式由于使用了new操作符 , 所以他会自动 return 新生成的对象
//伪代码 模拟寄生模式
 function Person(name, age) {
           let o1 = new Object();
            let obj = new Object();
            obj.name = name;
            obj.age = age;
            obj.sayname = function() {
                    console.log(this.name);
                }
            // return obj
            return o1
        }
        // let wyb = Person("xxx", 18)
        let wangyubo = new Person("yyy", 28)

在这里插入图片描述

  • 在寄生模式下 , Person里面类似于会有两个return , 如果自己定义了return的结果 , name就返回自己定义的结果 , 如果自己没有定义 , 那么也会按照new的默认步骤进行 , 最终拥有一个返回结果

标准一点的说法是 :

  • 构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个return 语句,可以重写调用构造函数时返回的值。
  • 如果不返回一个对象的话, 那么就如同是对一个普通的函数调用的new操作符, 此时Person的每一行代码照样会执行,只是丝毫不会影响新的对象
 function Person(name, age) {
            // let o1 = new Object();
            let obj = new Object();
            obj.name = name;
            obj.age = age;
            obj.sayname = function() {
                    console.log(this.name);
                }
                // return obj
                // return o1
        }

        // let wyb = Person("xxx", 18)
        let wangyubo = new Person("yyy", 28)

在这里插入图片描述

 function Person(name, age) {
            //构造函数模式
            this.name = name;
            this.age = age;
            this.sayName = function() {
                console.log(this.name);
            };

            //工厂模式
            let obj = new Object();
            obj.name = name;
            obj.age = age;
            obj.sayAge = function() {
                console.log(this.age);
            }
            return obj
        }

let wangyubo = new Person("yyy", 28)

在这里插入图片描述

当工厂模式有返回值的时候 , wangyubo就是由Object构造的

//return obj

在这里插入图片描述

此时执行的是sayName方法 , sayAge为undefined , 且wangyubo instanceof Person为true , 不过wangyubo instanceof Object也为true你觉得奇怪吗?

一点都不奇怪 , 此时的Object是Person的原型对象 , 而不是Person里面的那个Object了 , 不要想歪了

3.1 寄生构造函数的用法

这个模式可以在特殊的情况下用来为对象创建构造函数(可以将现在已经有的构造函数拿来用)。

假设我们想创建一个具有额外方法的特殊数组。由于直接修改Array 构造函数造成的后果和影响太大,我们就可以用这个方式来实现

function Person(name, age) {
 let a = new Array();
 a.name = name;
 a.age = age;
 a.sayname = function() {
     console.log(this.name);
 }
 return a
}
let wangyubo = new Person("yyy", 28)

此模式的问题:

  • 返回的对象与构造函数(Person)或者与构造函数的原型属性之间没有关系
  • 也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同
  • 为此,不能依赖instanceof 操作符来确定对象类型。
  • 由于存在上述问题,我们建议在可以使用其他模式的情况下,不要使用这种模式。

4. 稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this 的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this 和new),或者在防止数据被其他应用程序改动时使用

function Person(name, age) {
 let a = new Array();
 a.name = name;
 a.age = age;
 a.sayname = function() {
     console.log(name);
 }
 return a
}
let wangyubo = Person("yyy", 28)

在这里插入图片描述

稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:

  • 一是新创建对象的实例方法不引用this
  • 二是不使用new 操作符调用构造函数

在以这种模式创建的对象中,除了使用sayname()方法之外,没有其他办法访问name 的值 , 和闭包的理念类似

用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此instanceof 操作符对这种对象也没有意义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你是时光 轻轻呵唱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值