一、单例设计模式

把描述当前事物特征的信息进行分组归类,减少全局变量的污染。就是一个对象:不仅仅被叫做变量,也被称为命名空间

1. 单例模式

单例模式:把描述事物的信息放到一个命名空间中进行分组,防止全局变量的污染

  1. // 单例模式创建老师的信息记录对象
  2. var t1 = {
  3. name: '马宾',
  4. age: 18,
  5. subject: 'JS',
  6. from: '珠峰'
  7. };
  8. var t2 = {
  9. name: '姜文',
  10. age: 19,
  11. subject: '架构师课程',
  12. from: '珠峰'
  13. };
  14. // 单例模式虽然解决了全局变量命名空间互相覆盖的问题,但是效率太低,当大规模创建对象时,就需要写许多重复的代码。怎么解决这个问题?

2. 高级单例模式

为了让单例模式变得更高大上一些,真实项目中的单例模式都采用闭包的形式

  1. function teacher(name, age, subject, from = '珠峰') {
  2. var obj = {}; // 原材料
  3. obj.name = name; // 加工
  4. obj.age = age; // 加工
  5. obj.subject = subject;
  6. obj.from = from;
  7. obj.teach = function () {
  8. console.log(`${this.name} 老师教 ${this.subject}`);
  9. };
  10. return obj; // 出厂
  11. }
  12. var t3 = teacher('任金辉', 19, 'JS');
  13. console.log(t3);
  14. var t4 = teacher('薛振翔', 19, 'JS');
  15. console.log(t4);
  16. console.log(t3 === t4);
  17. // 工厂模式:像上面这样,把创建对象的细节封装成一个函数,在函数中为这个对象添加属性,这种创建对象的模式叫做工厂模式;
  18. // 工厂虽然可以批量生产,但是生产出来的对象都一样,没有分类。

二、工厂模式

批量化生产:把实现某个功能的代码进行封装,后期在想实现这个功能,我们直接执行函数即可

  • 低耦合:减少页面中冗余的代码
  • 高内聚:提高代码的重复使用
  1. var ary = new Array(1, 2, 3, 4); // 我们这样通过 new 操作符以实例创建方式创建了一个数组的实例?}
  2. console.log(ary);
  3. var t5 = new teacher('马宾', 18, 'JS', '珠峰');
  4. console.log(t5);
  5. // 我们通过 new 操作符执行工厂模式的函数,和直接执行没有啥区别;我们 new Array 得到数组实例,但是 new teacher 只能得到一个普通对象,和不 new 没区别;这个时候,咱们的 teacher 函数和 Array 存在
  6. 不同。
  7. // 因为 Array 虽然是函数数据类型的,但是不是普通函数,它叫做构造函数。

三、重构类的原型

  1. /*
  2. * 重构类的原型:让某个类的原型指向新的堆内存地址(重定向指向)
  3. * 问题:重定向后的空间中不一定有 CONSTRUCTOR 属性(只有浏览器默认给 PROTOTYPE 开辟的堆内存中
  4. 才存在 CONSTRUCTOR),
  5. 这样导致类和原型机制不完整;所以需要我们手动再给新的原型空间设置 CONSTRUCTOR 属性;
  6. * 问题:在重新指向之前,我们需要确保原有原型的堆内存中没有设置属性和方法,因为重定向后,
  7. 原有的属性和方法就没啥用了(如果需要克隆到新的原型堆内存中,我们还需要额外的处理) => 但是内置类的
  8. 原型,由于担心这样的改变会让内置的方法都消失,所以禁止了我们给内置类原型的空间重定向,例如:
  9. Array.prototype = {...} 这样没有用,如果想加方法 Array.prototype.xxx = function(){...} 可以这
  10. 样处理
  11. */
  12. function Fn() {
  13. // ...
  14. }
  15. Fn.prototype.xxx = function () {}
  16. // 批量给原型设置属性方法的时候:重构类的原型
  17. Fn.prototype = {
  18. constructor: Fn,
  19. getA: function () {},
  20. getB: function () {}
  21. };
  22. // 批量给原型设置属性方法的时候:设置别名
  23. let proto = Fn.prototype;
  24. proto.getA = function () {}
  25. proto.getB = function () {}
  26. proto.getC = function () {}
  27. proto.getD = function () {}

四、构造函数显示设置返回值

如果我们手动修改构造函数的返回值时;

  1. 如果 return 一个基本数据类型的值,没有任何影响,不会覆盖原有实例;
  2. 如果 return 引用数据类型,原有的实例就会背这个引用类型值覆盖

注意:慎重修改改造函数的返回值

五、调用和普通调用的区别

var address = ‘ ‘ 像这种,在实例中。这就是一个私有变量,不会添加到实例中。只有通过 this.xxx = xxx 这种方式才能将属性添加到实例中

new 操作符可以让函数执行,通过 new 调用,这个函数就会被当作构造函数对待,返回一个实例对象; new 执行和函数普通执行有很明显的区别。之所以会有这种区别,是因为函数的普通执行和 new 执行有很大区别

  • 普通函数执行机制
  1. 开辟私有作用域
  2. 形参赋值
  3. 变量提升
  4. 代码执行
  5. 销毁栈内存(特殊情况外)
  • new 构造函数执行:
  1. 开辟作用域
  2. 形参赋值
  3. 变量提升
  4. 隐式创建一个属于当前这个类的实例对象,然后把 this 指向这个实例对象
  5. 执行构造函数中的代码;如果 this.xxx = xxx; 就是给实例添加私有属性
  6. 隐式返回这个实例对象,相当于 return this;
  7. 销毁栈内存(构造函数的作用域销毁是否和普通函数一样)
  8. 构造函数中的 this,指向当前构造函数的实例对象;