1. 单例模式 Singleton Pattern

  1. 表现形式

    let nameSpace = { xxx : xxx };

  2. 作用

    把描述同一件事务的属性和特征进行”分组,归类”(存储在同一个堆内存中),避免全局变量之间的冲突和污染

  3. 高级单例模式

    在给命名空间赋值的时候,不是直接赋值一个对象,而是先执行匿名函数,形成一个私有作用域AA(不销毁的栈内存),在AA中创建一个堆内存,把堆内存地址赋值给命名空间 这种模式的好处:我们完全可以在AA中创造很多内容(变量OR函数),哪些需要供外面调取使用的,我们暴露到返回的对象中(模块化实现的一种思想)

  1. let nameSpace = (() => {
  2. function f() {}
  3. return {
  4. f
  5. }
  6. })();

2. 工厂模式 Factory Pattern

  1. 表现形式

    let createPerson = (name, age) => ({name, age}); let p1 = createPerson(‘korea’, 20), p2 = createPerson(‘demo’, 21); console.log(p1, p2);

  2. 作用

    • 1.把实现相同功能的代码进行“封装”,以此来实现“批量生产”(后期想要实现这个功能,我们只需要执行函数即可)
    • 2.“低耦合高内聚”:减少页面中的冗余代码,提高代码的重复使用率

3. 构造函数模式 constructor Pattern

  1. 表现形式

    function Fn(name, age) {
            let n  = 10;
            this.name = name;
            this.age = age;    
    }
    let f = new Fn('kroea', 20);
    
  2. 作用, 以及运行机制
    image-20200210141313139.png

    4. 原型链设计模式 prototype Pattern

  3. 概括

    • 原型(prototype)、原型链(proto
    • [函数]
      • 普通函数、类(所有的类:内置类、自己创建的类)
    • [对象]
      • 普通对象、数组、正则、Math、arguments…
      • 实例是对象类型的(除了基本类型的字面量创建的值)
      • prototype的值也是对象类型的
      • 函数也是对象类型的
  4. 运行机制

    • 所有的函数数据类型都天生自带一个属性:prototype(原型),这个属性的值是一个对象,浏览器会默认给它开辟一个堆内存
    • 在浏览器给prototype开辟的堆内存中有一个天生自带的属性:constructor,这个属性存储的值是当前函数本身
    • 每一个对象都有一个__proto__的属性,这个属性指向当前实例所属类的prototype(如果不能确定它是谁的实例,都是Object的实例)
  5. 原理图

image-20200210151506445.png

  1. 案例
function Fn() {
    var n = 100;
    this.AA = function () {
        console.log(`AA[私]`);
    };
    this.BB = function () {
        console.log(`BB[私]`);
    };
}
Fn.prototype.AA = function () {
    console.log(`AA[公]`);
};
var f1 = new Fn;
var f2 = new Fn;
console.log(f1.n);

image-20200210152033867.png

5. 发布订阅模式 Publish subscription mode

  1. 概念
    • 准备一个容器,把到达指定时候要处理的事情,事先一一的增加到容器中(发布计划,并且向计划表中订阅方法), 当到达指定时间点,通知容器中的方法依次执行即可
  2. 代码实现
~function anonymous(window) {
    class Subscribe {
        constructor() {
            //=>创建一个容器
            //每一个实例都有一个自己独有的容器,管理自己需要执行的方法即可
            this.pond = [];
        }

        //=>向计划表(POND池子)中增加方法:去重
        //FN:我们需要增加的方法
        add(fn) {
            let pond = this.pond,
                isExist = false;
            pond.forEach(item => item === fn ? isExist = true : null);
            !isExist ? pond.push(fn) : null;
        }

        //=>从计划表(POND池子)中移除方法
        remove(fn) {
            let pond = this.pond;
            pond.forEach((item, index) => {
                if (item === fn) {
                    // pond.splice(index, 1);//=>我们不能基于SPLICE删除,因为这种删除方式会改变原有的数组,例如:我们通知方法执行,当执行到FN3的时候(FIRE循环索引是2),但是基于SPLICE把FN1/FN2删除后,原始数组后面的项都向前提取两位,此时FIRE中继续遍历下一个方法(索引3),已经找不到和他匹配的那一项了

                    //=>让当前项赋值为NULL即可(这样函数移除掉了,但是此时的数组结构没有改变,不会出现数组塌陷的问题)
                    pond[index] = null;//=>item=null是不行的
                }
            });
        }

        //=>通知计划表中的方法依次执行
        //如果传递参数信息了,把这些参数依次赋值给执行的每一个方法
        fire(...arg) {
            let pond = this.pond;
            //=>REMOVE机制处理了,此时ITEM不一定都是函数了,还有可能是NULL
            //NULL的话不执行,而且最好是把这一项删除掉
            for (let i = 0; i < pond.length; i++) {
                let item = pond[i];
                if (item === null) {
                    pond.splice(i, 1);
                    i--;
                    continue;
                }
                item(...arg);
            }
        }
    }

    window.Subscribe = Subscribe;
}(window);
  1. 发布订阅中数组塌陷

发布订阅中的数组塌陷问题.png