js设计模式

文章介绍了JavaScript中常见的设计模式,包括单例模式用于确保类只有一个实例,工厂模式用于创建对象而无需直接使用new关键字,以及观察者模式实现对象间一对多的依赖关系。这些模式有助于提升代码的灵活性和可维护性。
摘要由CSDN通过智能技术生成

JavaScript设计模式是为了解决JavaScript代码中常见问题的可重用解决方案。

以下是一些常见的JavaScript设计模式:

一、单例模式(Singleton Pattern)

确保一个类只有一个实例,并提供一个全局访问点。在 JavaScript 中,可以使用闭包来实现单例模式。

var Singleton = (function() {
  var instance;

  function createInstance() {
    var object = new Object("I am the instance");
    return object;
  }

  return {
    getInstance: function() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

// 使用
var instance1 = Singleton.getInstance();
var instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

在这个例子中,Singleton 是一个立即执行函数,它返回一个对象,其中包含一个getInstance方法。getInstance方法首先检查是否已经存在实例,如果没有,则创建一个新的实例并返回它。如果已经存在实例,则直接返回它。

由于 JavaScript 中的函数都是闭包,所以instance变量可以被createInstancegetInstance方法共享,这样就可以保证只有一个实例存在。

注意:在使用单例模式时,需要注意不要滥用它,否则会导致代码变得复杂和难以维护。


二、工厂模式(Factory Pattern)

JavaScript 工厂模式是一种创建对象的设计模式,它通过工厂方法来创建和返回对象,而无需直接在代码中使用 new 关键字。工厂模式可以将对象的创建与使用分离,使得代码更加灵活和可维护。

function createPerson(name, age) {
  const person = {};
  person.name = name;
  person.age = age;
  person.sayHello = function() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  };
  return person;
}

const john = createPerson('John', 30);
john.sayHello(); // 输出 "Hello, my name is John and I'm 30 years old."

在这个示例中,我们定义了一个名为 createPerson 的工厂函数,用于创建 Person 对象。该函数接受两个参数:name 和 age,用于设置 Person 对象的属性。在函数内部,我们使用一个空对象 person 来创建 Person 对象,并将其属性设置为传递的参数。最后,我们将一个新的方法 sayHello 添加到 person 上,并将其返回。

通过使用工厂模式,我们可以通过调用 createPerson 函数来创建多个 Person 对象,而无需重复编写相同的代码。


三、观察者模式(Observer Pattern)

又叫做发布-订阅模式(Publish-Subscribe Pattern);定义了对象之间的一对多依赖关系,使得当一个对象改变状态时,所有依赖它的对象都会被通知并自动更新。

// 主题对象
class Subject {
  constructor() {
    this.observers = [];
  }
  // 添加观察者
  addObserver(observer) {
    this.observers.push(observer);
  }
  // 移除观察者
  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }
  // 通知观察者
  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}
// 观察者对象
class Observer {
  update(data) {
    console.log(`Received data: ${data}`);
  }
}
// 创建主题对象
const subject = new Subject();
// 创建观察者对象
const observer1 = new Observer();
const observer2 = new Observer();
// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
// 通知观察者
subject.notify('Hello world!');

在这个示例中,我们创建了一个主题对象Subject和两个观察者对象Observer,然后将观察者对象添加到主题对象的观察者列表中。当主题对象的状态发生变化时,它会通知观察者对象,并执行它们的更新方法。在这个示例中,我们通过调用subject.notify('Hello world!')来通知观察者对象,并将字符串'Hello world!'作为参数传递给观察者对象的更新方法。


四、装饰者模式(Decorator Pattern)

用于动态地给一个对象添加额外的职责,同时又不影响该对象的其他功能。

// 定义一个普通的对象
const obj = {
  name: 'John',
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

// 定义一个装饰器函数
function addAge(obj, age) {
  obj.age = age;
  obj.sayAge = function() {
    console.log(`I am ${this.age} years old`);
  };
  return obj;
}

// 使用装饰器函数为对象添加新的属性和方法
const decoratedObj = addAge(obj, 30);

// 调用对象的方法和新添加的方法
decoratedObj.sayHello(); // 输出:Hello, my name is John
decoratedObj.sayAge();   // 输出:I am 30 years old

 在上面的例子中,我们定义了一个普通的对象 obj,然后使用装饰器函数 addAge 为它添加了新的属性 age 和方法 sayAge。通过这种方式,我们成功地扩展了对象 obj 的功能,而不需要修改原始对象的代码。这个例子只是装饰器模式的一个简单示例,实际应用中可能会更加复杂。


五、适配器模式(Adapter Pattern)

将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能在一起工作的类可以协同工作。

参考:https://aiguangyuan.blog.csdn.net/article/details/103212133

var chinaPlug = {
    type: '中国插头',
    chinaInPlug() {
        console.log('开始供电');
    }
};
 
var japanPlug = {
    type: '日本插头',
    japanInPlug() {
        console.log('开始供电');
    }
};
 
// 日本插头电源适配器
function japanPlugAdapter(plug) {
    return {
        chinaInPlug() {
            return plug.japanInPlug();
        }
    }
};
 
japanPlugAdapter(japanPlug).chinaInPlug();
// 开始供电

 六、策略模式(Strategy Pattern)

定义了一系列算法,并将每个算法封装起来,使它们可以互相替换,使得算法的变化不会影响到使用算法的客户。


const StrategyMap = {};
StrategyMap.minus100_30 = function(price) { 
  	return price - Math.floor(price / 100) * 30
};
function context(type, ...rest) {
    return StrategyMap[type] && StrategyMap[type](...rest)
};
context('minus100_30', 270);
// 210

 根据上面的例子提炼一下策略模式,折扣计算方式可以被认为是策略(Strategy),这些策略之间可以相互替代,而具体折扣的计算过程可以被认为是封装上下文(Context),封装上下文可以根据需要选择不同的策略。

主要有下面几个概念:

1. Context :封装上下文,根据需要调用需要的策略,屏蔽外界对策略的直接调用,只对外提供一个接口,根据需要调用对应的策略;

2. Strategy :策略,含有具体的算法,其方法的外观相同,因此可以互相代替;

3. StrategyMap :所有策略的合集,供封装上下文调用;


七、命令模式(Command Pattern)

将一个请求封装为一个对象,使得可以用不同的请求对客户进行参数化,同时将请求排队或记录请求日志,以及支持可撤销的操作。

八、职责链模式(Chain of Responsibility Pattern)

将请求的发送者和接收者解耦,使得多个对象都有机会处理这个请求,从而避免了请求发送者需要知道哪一个对象会处理请求的问题。

九、模板方法模式(Template Method Pattern)

定义了一个操作中的算法骨架,将一些步骤延迟到子类中实现,使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。

十、迭代器模式(Iterator Pattern)

提供一种访问一个容器对象中各个元素的方法,而又不暴露该对象的内部细节。

十一、代理模式(Proxy Pattern)

又称委托模式;为另一个对象提供一个替身或占位符,以便控制对原对象的访问。可以通过代理对象来控制对原对象的访问权限,或者在访问前后添加一些额外的逻辑。


// 明星
const SuperStar = {
    name: '小鲜肉',
    // 档期标识位,false-没空,true-有空
    scheduleFlag: false,           
    playAdvertisement(ad) {
        console.log(ad)
    }
}
 
// 经纪人
const ProxyAssistant = {
    name: '经纪人',
    scheduleTime(ad) {
        // 在这里监听 scheduleFlag 值的变化
        const schedule = new Proxy(SuperStar, { 			
            set(obj, prop, val) {
                if (prop !== 'scheduleFlag') {return};
                // 小鲜肉现在有空了
                if (obj.scheduleFlag === false && val === true) {                    
                    obj.scheduleFlag = true;
                    // 安排上了
                    obj.playAdvertisement(ad);        
                }
            }
        });
        setTimeout(() => {
            console.log('小鲜鲜有空了');
            // 明星有空了
            schedule.scheduleFlag = true              
        }, 2000)
    },
    playAdvertisement(reward, ad) {
        // 如果报酬超过100W
        if (reward > 1000000) {             
            console.log('没问题,我们小鲜肉最喜欢拍广告了')
            ProxyAssistant.scheduleTime(ad);
        } else{
            console.log('没空,滚!');
        }
 
    }
}
 
ProxyAssistant.playAdvertisement(10000, '纯蒸酸牛奶,味道纯纯,尽享纯蒸');
// 没空,滚
ProxyAssistant.playAdvertisement(1000001, '纯蒸酸牛奶,味道纯纯,尽享纯蒸');
// 没问题,我们小鲜肉最喜欢拍广告了
// 小鲜肉有空了
// 纯蒸酸牛奶,味道纯纯,尽享纯蒸

以上是一些常见的JavaScript设计模式,它们可以帮助我们提高代码的可重用性、可维护性和可扩展性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值