JS设计模式-单例、工厂、代理、发布订阅

·  阅读 1084

单例模式

保证一个类仅有一个实例,并提供访问此实例的全局访问点。

const Singleton = function () {}
Singleton.getInstance = (function(){
   //es6没有静态类型,利用闭包,函数外部无法访问instance
   let instance = null;
   return function(){
       if(!instance){
           instance = new Singleton()
       }
       //如果已经存在则直接返回
       return instance
   };
})()
let s1 = Singleton.getInstance()
let s2 = Singleton.getInstance()
console.log(s1===s2)   //true
复制代码

工厂模式

工厂方法模式的实质是定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。

class Dog {  //实体类
    run(){
        console.log('Dog')
    }
}
class Cat {   //实体类
    run(){
        console.log('Cat')
    }
}
class Animal{  //工厂类
    constructor(name){
        switch (name){
            case 'Dog':
                return new Dog();
            case 'Cat':
                return new Cat();
            default:
                throw TypeError('class name wrong')
        } 
    }
}
const cat = new Animal('Cat')
cat.run()   
const dog = new Animal('Dog')
dog.run()
复制代码

发布订阅模式

定义了对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都可以得到通知。

// vue2.0响应式实现
//判断是否为对象
function isObject(obj) {
    return typeof obj === 'object' && obj !== null
};
/**
 * @param {*} obj
 */

//监听对象变化
function observe(obj) {
    if (!isObject(obj)) {
        return;
    }
    Object.keys(obj).forEach(key => {
        var originValue = obj[key]
        observe(originValue)
        var dep = new Dep() //针对每一个属性创建一个依赖收集
        Object.defineProperty(obj, key, {
            get: function() {
                // console.log('get' + originValue)
                dep.depend()
                return originValue
            },
            set: function(value) {
                if (value !== originValue) {
                    // console.log('set', value)
                    originValue = value
                    observe(originValue)
                    dep.notify()
                }
            }
        })
    })
}
//test
var state = {
    msg: 'hello',
    mount: 10,
    addstr: {
        name: 'jiangyao',
        age: 21
    }
};
observe(state)

//依赖收集
function Dep() {
    this.functions = new Set()
}
var activeFunction = null
//有某个函数在用我
Dep.prototype.depend = function() {
    if (activeFunction) {
        this.functions.add(activeFunction)
    }
}

//通知用到这个函数的所有对象:我变了
Dep.prototype.notify = function() {
    this.functions.forEach(fn => fn())
}

function autorun(fn) {
    function functionWrapper() {
        activeFunction = functionWrapper
        fn()
        activeFunction = null
    }
    functionWrapper()
}

autorun(() => {
    app.innerHTML = `${state.msg}+${state.mount}`   //添加到页面
})
复制代码
    <div id="app"></div>
    <script src="./vue响应式.js"></script>
复制代码

test.gif

代理模式

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

ES6所提供Proxy构造函数能够让我们轻松的使用代理模式:

var proxy = new Proxy(target, handler);

target要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

handler一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

//简单实现验证
let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    // The default behavior to store the value
    obj[prop] = value;

    // 表示成功
    return true;
  }
};

let person = new Proxy({}, validator);
person.age = 100;
console.log(person.age);
// 100

person.age = 'young';
// 抛出异常: Uncaught TypeError: The age is not an integer
person.age = 300;
// 抛出异常: Uncaught RangeError: The age seems invalid
复制代码
分类:
前端