【javascript】设计模式

为什么别人写得代码又好看又好用?

有人说从“写代码”到“写好代码”到“设计代码”,不仅是技术的提升,更是编程思维的提升。现在想想很道理,记得刚毕业时,写代码就单纯为了实现功能,虽然有时也会模仿别人结构,但是对功能结构了解得很浅,渐渐地开始注重代码的复用性和可读性,重新回顾一下设计模式,才看懂了别人用的结构,另外面试也会出手写设计模式的题目哦~

概念

在这里插入图片描述

实例

单例模式

思路:在构造函数返回对象之前,判断是否已经实例化,使其只在内存中创建一次

  • new.target 或者 instanceof
    1)在类的构造方法中,new.target指向构造方法类名
    2) instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。 语法 object instanceof constructor
class User {
    constructor() {
        if (new.target !== User) return;
        // if (!(this instanceof User)) return;
        if (!User._instance) {
            this.name = 'hh';
            User._instance = this
        }
        return User._instance
    }
}
// function User() {
//     if (!(this instanceof User))return
//     if (!User._instance) {
//         this.name = '无名'
//         User._instance = this//当前函数的实例
//     }
//     return User._instance
// }
const u1 = new User()
const u2 = new User()
console.log(u1===u2);// true
  • static静态方法(类方法,访问方式:类名.方法名)
class User {
    constructor() {
        this.name = 'hh'
    }
    static getInstance() {
        if (!User._instance) User._instance = this
        return User._instance
    }
}
const u1 = User.getInstance()
const u2 = User.getInstance()
console.log(u1 === u2);// true
  • 闭包
function User() {
    this.name = '无名'
}
User.getInstance = (function () {
    var instance
    return function () {
        if (!instance) {
            instance = new User()
        }
        return instance
    }//闭包
})()
const u1 = User.getInstance()
const u2 = User.getInstance()
console.log(u1 === u2);// true

原型模式

思路:在原型链上扩展方法,使其子代得以继承

  • prototype
const Parents = {
    name: '姚',
    life: function () {
        console.log(this.name + '要工作学习');
    }
}
function createKid(name) {
    function kid() { };
    kid.prototype = Parents;
    let kid1 = new kid();
    kid1.name = kid1.name + name;
    return kid1
}
let kid1 = createKid('小孩');
kid1.life(); //姚小孩要工作学习

工厂模式 1

思路:把产品属性和实例化过程封装到工厂里,通过传参就可以获取对象

class Factory {
    constructor(opt) {
        this._type = opt.type; //产品名称
    }

    static createProduct(param) {
        //静态方法:直接通过类来调用,不会被实例继承,this指的是类不是实例对象
        switch (param) {
            case 'product1':
                return new Factory({ type: '产品1' });
            case 'product2':
                return new Factory({ type: '产品2' });
            default:
                throw new Error('参数错误, 可选参数:product1、product2')
        }
    }
    getProductType() {
        return this._type;
    }
}

let p1 = Factory.createProduct('product1');
console.log(p1.getProductType());
let p2 = Factory.createProduct('product2');
console.log(p2.getProductType());

用户权限设置就可以使用简单工厂模式,见参考 1
注意:使用this.$router.addRoutes方法添加的路由刷新后不能保存,需要本地保存用户信息在页面刷新时重新加载路由

装饰模式

思路:封装一个方法包含新的和旧的逻辑

var oldCloth = function () {
    console.log('我是一堆你看不懂的老逻辑');
}
var newCloth = function () {
    console.log('我是新的逻辑');
}
wear = function () {
    _oldCloth();
    newCloth();
}
wear();

代理模式2

思路:就是在原功能基础上扩充,使用代理访问原功能和扩充功能

  • 图片预加载
var myImage = (function () {
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrc: function (src) {
            imgNode.src = src
        }
    }
})();

var proxyImage = (function () {
    var img = new Image()
    img.onload = function () {
        myImage.setSrc(img.src) //当img.src加载完成执行这个操作
    }
    return {
        setSrc: function (src) {
            img.src = src
            myImage.setSrc('/loading.gif') //预加载loading图片
        }
    }
})();
proxyImage.setSrc('/real.png');
var mult = function () {
    console.log('开始计算乘积');
    var a = 1;
    for (var i = 0, l = arguments.length; i < l; i++) {
        a = a * arguments[i];
    }
    return a;
};
var proxyMult = (function () {
    var cache = {}
    return function () {
        // arguments是对象[Arguments] { '0': 2, '1': 3 }
        let id = Array.prototype.join.call(arguments, ',')
        console.log(typeof id);//string
        console.log(id);//"2,3"
        if (cache[id]) {
            return cache[id]
        } else {
            return cache[id] = mult.apply(this, arguments) //缓存中记录{"2,3":6}
        }
    }
})()
proxyMult(2, 3); // 输出:6
  • 基于VUE3的Proxy
//明星
let star = {
    name: '张XX',
    age: 25,
    phone: '13910733521'
}

// 经纪人
let agent = new Proxy(star, {
    get: function (target, key) {
        if (key === 'phone') {
            // 返回经纪人自己的手机号
            return '18611112222'
        }
        if (key === 'price') {
            // 明星不报价,经纪人报价
            return 120000
        }
        return target[key]
    },
    set: function (target, key, val) {
        if (key === 'customPrice') {
            if (val < 100000) {
                // 最低 10w
                throw new Error('价格太低')
            } else {
                target[key] = val
                return true
            }
        }
    }
})
// 主办方
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)// 想自己提供报价(砍价,或者高价争抢)
agent.customPrice = 150000
// agent.customPrice = 90000  // 报错:价格太低
console.log('customPrice', agent.customPrice)

发布订阅模式

思路:明确角色(发布者、订阅者)–> 订阅方法(存储订阅者事件,用于之后消息的接收) --> 触发方法(遍历订阅者存储的回调函数)

  • 观察者模式(区别:订阅者和发布者不关联)
class Subject {
    constructor() {
        this.subs = [];
    }
    addSub(sub) {//订阅
        this.subs.push(sub);
    }
    notify() {//触发
        this.subs.forEach(sub => {
            sub.update(); //目标和观察的联系
        });
    }
}
class Observer {
    update() {
        console.log('update');
    }
}
let subject = new Subject();
let ob = new Observer();
//目标添加观察者了
subject.addSub(ob);
//目标发布消息调用观察者的更新方法了
subject.notify();   //update
  • 发布订阅模式(有个事件通道也叫调度中心)
class PubSubEvent {
    constructor() {
        this._listenerStore = {};//事件存储
    }

    onMessage(key, fn) { //订阅事件,fn回调函数为事件通道
        if (this._listenerStore[key]) {
            this._listenerStore[key].push(fn);
        } else {
            this._listenerStore[key] = [fn];
        }
    }

    getOnceMessage(tempListener) {
        this._tempListener = tempListener;
    }

    off(key, fn) { //取消订阅
        //delete只能删除对象自己的属性,不能删除其原型链上的属性,全局作用域中的函数不能被delete
        if (this._listenerStore[key]) delete this._listenerStore[key];
    }

    async start() {//触发事件
        // setTimeout(() => {
        Object.values(this._listenerStore).forEach(arr =>
            arr.forEach(_fun => _fun(Math.random() * 100 + '元'))
        )
        if (this._tempListener) {
            this._tempListener('只订阅者一次性:' + Math.random() * 100 + '元');
            delete this._tempListener;
        }
        // }, 5000);
    }

}

let subscriber = new PubSubEvent();
subscriber.onMessage('张三', (price) => {
    console.log('当前价格' + price);
})
let publisher = new PubSubEvent();
publisher.start();

迭代器模式3

思路:遍历对象,回调返回

  • 内部迭代,如:重写forEach
var each = function(arr, callback) {
    var result
    for (var index = 0, len = arr.length; index < len; index++) {
        result = callback.call(arr, index, arr[index])
        //call(指定函数上下文对象thisobj,参数args)
        if (result === false) {
            break
        }
    }
}

each([1,2,3,3,4,5,6,7], function(index, item) {
    if (item > 3) {
        return false
    }
})
  • 外部迭代,如:Iterator,通过ltertor.next()迭代下一个元素,可以手工控制迭代过程或者顺序
var Iterator=function(obj){
    var current=0;
    var next=function(){
        current +=1;
    }
    var isDone=function(){
        return current >= obj.length
    }
    var getCurrent=function(){
        return obj[current];
    }
    return {
        next,
        length:obj.length,
        isDone,
        getCurrent
    }
}
var compare = function(iteraotr1, iteraotr2) {
    if (iteraotr1.length !== iteraotr2.length) {
        alert('不相等')
    }
    //外部设定条件来决定迭代器的进行
    while (!iteraotr1.isDone() && !iteraotr2.isDone()) {
        if (iteraotr1.getCurrent() !== iteraotr2.getCurrent()) {
            alert('不相等')
        }
        iteraotr1.next()
        iteraotr2.next()
    }
    alert('相等')
}

var iteraotr1 = Iterator([1,2,3])
var iteraotr2 = Iterator([1,2,3])
compare(iteraotr1,iteraotr2) // 相等

参考


  1. JavaScript 设计模式(三):工厂模式 ↩︎ ↩︎

  2. JavaScript 设计模式(八):代理模式 ↩︎

  3. JavaScript 设计模式(十四):迭代器模式 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值