工厂模式
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。 工厂模式 可以简单理解为在函数内部创建了一个新的空对象,并为其添加属性和方法。
function createObject(name, age, profession) {//集中实例化的函数\
var obj = new Object();
obj.name = name;
obj.age = age;
obj.profession = profession;
obj.move = function () {
return this.name + ' at ' + this.age + ' engaged in ' + this.profession;
};
return obj;
}
var test1 = createObject('trigkit4', 22, 'programmer');//第一个实例
var test2 = createObject('mike', 25, 'engineer');//第二个实例
console.log(test1.move());
console.log(test2.move());
console.log(test1.move() === test2.move()) //false
缺点: 虽然new的是共同的一个函数,但是其中的方法做对比,返回false,也就意思说,new的函数,中的方法,不是共享(不是同一个地址).
构造函数模式
构造函数模式和工厂模式具有共同的 缺点,都是 虽然new的是共同的一个函数,但是其中的方法做对比,返回false,也就意思说,new的函数,中的方法,不是共享(不是同一个地址). ,但是他们还是有些不同的地方。
function CreateObject(name, age, profession) {//集中实例化的函数\
this.name = name;
this.age = age;
this.profession = profession;
this.move = function () {
return this.name + ' at ' + this.age + ' engaged in ' + this.profession;
};
}
var test3 = new CreateObject('trigkit4', 22, 'programmer');//第一个实例
var test4 = new CreateObject('mike', 25, 'engineer');//第二个实例
console.log(test3.move());
console.log(test4.move());
console.log(test3.move() === test4.move()) //false
class CreateObject {
constructor(name, age, profession){
this.name = name;
this.age = age;
this.profession = profession;
}
move = ()=>{
return this.name + ' at ' + this.age + ' engaged in ' + this.profession;
}
}
var test3 = new CreateObject('trigkit4', 22, 'programmer');//第一个实例
var test4 = new CreateObject('mike', 25, 'engineer');//第二个实例
console.log(test3.move());
console.log(test4.move());
console.log(test3.move() === test4.move()) //false
(1):构造函数模式不需要在内部创建对象。
(2):构造函数模式 函数的首字母要大写。
(3):构造函数模式 内写的方法和属性前用 this
原型模式
每当我们创建一个函数,都有一个prototype 属性
class CreateObject {
constructor(name, age, profession){
this.name = name;
this.age = age;
this.profession = profession;
}
}
CreateObject.prototype.move = ()=>{
return this.name + ' at ' + this.age + ' engaged in ' + this.profession;
}
var test3 = new CreateObject('trigkit4', 22, 'programmer');//第一个实例
var test4 = new CreateObject('mike', 25, 'engineer');//第二个实例
console.log(test3.move());
console.log(test4.move());
console.log(test3.move() === test4.move()) //true
单例模式
单例模式的意思是,保证一个类只有一个实例,并且有一个接口供全局访问。它的作用就是防止频繁创建实例,浪费不必要的内存空间和资源消耗,那它有什么实用场景呢,假如我们在页面中有一个点击跳出一个弹窗操作,弹窗应该是唯一的,无论点击多少次它都应该被构建一次,那么这个弹窗就适合用单例模式来创建。
如何才能保证一个类仅有一个实例?
需要构造函数具备判断自己是否已经创建过一个实例的能力
class Dog {
show() {
console.log('我是一个单例对象')
}
static getInstance() {
// 判断是否已经new过1个实例
if (!Dog.instance) {
// 若这个唯一的实例不存在,那么先创建它
Dog.instance = new SingleDog()
}
// 如果这个唯一的实例已经存在,则直接返回
return Dog.instance
}
}
const s1 = Dog.getInstance()
const s2 = Dog.getInstance()
// true
s1 === s2
实现思想:将第一次创建的实例进行保存,之后再次创建前判断是否已经创建,如果之前创建过则返回已经保存的实例,否则创建一个实例,将实例创建和判断封装到了一个 getInstance 函数中,这种方式相对简单,但增加了类的“不透明性”,用一个函数来获取一个实例,而不是以往通过 new 来创建。
其他创建单例模式方法:blog.csdn.net/SK_study/ar…
// 先实现一个基础的StorageBase类,把getItem和setItem方法放在它的原型链上
function StorageBase () {}
// 以闭包的形式创建一个引用自由变量的构造函数
const Storage = (function(){
let instance = null
return function(){
// 判断自由变量是否为null
if(!instance) {
// 如果为null则new出唯一实例
instance = new StorageBase()
}
return instance
}
})()
// 这里其实不用 new Storage 的形式调用,直接 Storage() 也会有一样的效果
const storage1 = new Storage()
storage1.name = 123
const storage2 = new Storage()
console.log(storage2);
// 返回true
console.log(storage1 === storage2);
装饰器模式
在开发过程中,很多时候我们不想要类的功能一开始就很庞大,一次性包含很多职责。这个时候我们可以使用装饰器模式。动态的给某个对象添加一些职责,并且不会影响从这个类派生的其他对象。
在传统的面向对象开发中,给对象添加功能时,我们通常会采用继承的方式,继承的方式目的是为了复用,但是随之而来也带来一些问题:
(1)父类和子类存在强耦合的关系,当父类改变时,子类也需要改变;
(2)子类需要知道父类中的细节,至少需要知道接口名,从而进行复用或复写,这样其实破坏了封装性;
(3)继承的方式,可能会创建出大量子类。比如现在有BBA三种类型的汽车,构造了一个汽车基类,三个三种类型的汽车。现在需要给汽车装上雾灯、前大灯、导航仪、刮雨器,如果采用继承的方式,那么就要构建3*4个类。但是如果把雾灯、前大灯、导航仪、刮雨器动态地添加到汽车上,那么只需要增加4个类。这种采用动态添加职责的方式就是装饰器。
装饰器的目的就是在不改变原来类的基础上,为其在运行期间动态的添加职责。
装饰器用法
类装饰器
// 装饰器函数,它的第一个参数是目标类
function classDecorator(target) {
target.hasDecorator = true
return target
}
// 将装饰器“安装”到Button类上
@classDecorator
class Button {
// Button类的相关逻辑
}
// 验证装饰器是否生效
console.log('Button 是否被装饰了:', Button.hasDecorator)
函数装饰器
function funcDecorator(target, name, descriptor) {
// target: Button.prototype name: 修饰的目标属性属性名 descriptor:JavaScript提供的一个内部数据结构、一个对象,专门用来描述对象的属性
let originalMethod = descriptor.value
descriptor.value = function() {
console.log('我是Func的装饰器逻辑')
return originalMethod.apply(this, arguments)
}
return descriptor
}
class Button {
@funcDecorator
onClick() {
console.log('我是Func的原有逻辑')
}
}
// 验证装饰器是否生效
const button = new Button()
button.onClick()
参数装饰器,属性装饰器
属性修饰器 > 参数修饰器 > 方法修饰器 > 类修饰器
修饰器对类行为的改变,是代码编译时发生的,而不是在运行时。修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数
适配器模式
适配器模式 可以用来在现有接口和不兼容的类之间进行适配。
很多常见的包中都会有适配器模式;
代理模式
代理模式是非常常见的模式,比如我们使用的VPN工具,明星的经纪人,都是代理模式的例子。但是,有人会疑问,明明可以直接访问对象,为什么中间还要加一个壳呢?这也就说到了代理模式的好处。在我看来,代理模式最大的好处,就是在不动原有对象的同时,可以给原有对象增加一些新的特性或者行为。
事件代理:根据事件冒泡的特性实现代理;
虚拟代理
缓存代理
保护代理
策略模式
策略模式的定义:定义一系列的算法,将他们一个个封装起来,使他们直接可以相互替换。
观察者模式
观察者和被观察者,是松耦合的关系
// 定义发布者类
class Publisher {
constructor() {
this.observers = []
console.log('Publisher created')
}
// 增加订阅者
add(observer) {
console.log('Publisher.add invoked')
this.observers.push(observer)
}
// 移除订阅者
remove(observer) {
console.log('Publisher.remove invoked')
this.observers.forEach((item, i) => {
if (item === observer) {
this.observers.splice(i, 1)
}
})
}
// 通知所有订阅者
notify() {
console.log('Publisher.notify invoked')
this.observers.forEach((observer) => {
observer.update(this)
})
}
}
// 定义订阅者类
class Observer {
constructor() {
console.log('Observer created')
}
update() {
console.log('Observer.update invoked')
}
}
const use1 = new Observer();
const use2 = new Observer();
const use3 = new Observer();
const Person = new Publisher();
Person.add(use1);
Person.add(use2);
Person.add(use3);
Person.notify();
订阅-发布模式
发布者和订阅者,则完全不存在耦合
//发布订阅模式
class EventEmiter {
constructor() {
//维护一个对象
this._events = {
}
}
on(eventName, callback) {
if (this._events[eventName]) {
//如果有就放一个新的
this._events[eventName].push(callback);
} else {
//如果没有就创建一个数组
this._events[eventName] = [callback]
}
}
emit(eventName, ...rest) {
console.log(...rest + 'rest的写法')
// alert(...rest)
if (this._events[eventName]) { //循环一次执行
this._events[eventName].forEach((item) => {
item.apply(this, rest)
});
}
}
removeListener(eventName, callback) {
alert(callback)
if (this._events[eventName]) {
//当前数组和传递过来的callback相等则移除掉
this._events[eventName] =
this._events[eventName].filter(item => item !== callback);
}
}
once(eventName, callback) {
function one() {
//在one函数运行原来的函数,只有将one清空
callback.apply(this, arguments);
//先绑定 执行后再删除
this.removeListener(eventName, one);
}
this.on(eventName, one);
//此时emit触发会执行此函数,会给这个函数传递rest参数
}
}
class Man extends EventEmiter { }
let man = new Man()
function chifan() {
console.log('我吃了' + JSON.stringify(...arguments));
}
man.on('ele', chifan)//ele ,绑定一个函数方法
// man.removeListener('ele', chifan); //移除一个函数方法
man.emit('ele', ['米饭', '面条']);
//绑定一次,触发多次,也只执行一次。触发后一次将数组中的哪一项删除掉下次触发就不会执行
迭代器模式
迭代器模式是设计模式中少有的目的性极强的模式。所谓“目的性极强”就是说它不操心别的,它就解决这一个问题——遍历。
在ES6中,针对Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象这些原生的数据结构都可以通过for...of...进行遍历。
之所以能够按顺序一次一次地拿到数组里的每一个成员,是因为我们借助数组的Symbol.iterator生成了它对应的迭代器对象,通过反复调用迭代器对象的next方法访问了数组成员,像这样: