JavaScript设计模式手写示例讲解

   2023-02-08 学习力0
核心提示:目录建造者模式装饰者模式工厂模式适配器模式单例模式惰性单例模式总结想分享的几种设计模式目前模式:工厂模式,单例模式,适配器模式,装饰者模式,建造者模式建造者模式简介:建造者模式(builder pattern)比较简单,它属于创建型模式的一种。白话:4个部

想分享的几种设计模式

目前模式:工厂模式,单例模式,适配器模式,装饰者模式,建造者模式

建造者模式

简介:建造者模式(builder pattern)比较简单,它属于创建型模式的一种。

白话:4个部分:有个产品,有个工厂可以造产品,有个设计师指挥造多少,有个人想买产品。

买产品的用户不介意产品制造流程,只需要产品!

function Cola() {
    this.sugar = '50g',
    this.water = '100g'
}
function Packing() { // 第一种打包方式
    this.createPkg = function(){
        console.log('创建可乐外皮')
    }
    this.pushCola = function() {
        console.log('可乐倒进瓶子')
    }
    this.complete = function() {
        var cola = new Cola()
        cola.complete = true
        return cola
    }
    this.init = function() {
        this.createPkg() // 创建外皮
        this.pushCola() // 倒进瓶子
        //还可以增加其他步骤
        return this.complete() // 制作完成
    }
}
function greenPacking() { //绿皮可乐打包方式
    this.createPkg = function(){
        console.log('创建green可乐外皮')
    }
    this.pushCola = function() {
        console.log('可乐倒进green瓶子')
    }
    this.complete = function() {
        var cola = new Cola()
        cola.complete = true
        return cola
    }
    this.init = function() {
        this.createPkg() // 创建外皮
        this.pushCola() // 倒进瓶子
        //还可以增加其他步骤
        return this.complete() // 制作完成
    }
}
function Boss() {
    this.createCola = function(packType) {
        const pack = new window[packType]
        this.product = pack.init() //完整产品产出
    }
    this.getCola = function(packType) {
        this.createCola(packType);
        return this.product
    }
}
const boss = new Boss()
var UserCola = boss.getCola('greenPacking') // UserCola.complete === true

其他东西都不要,只要最后生产好的Cola,有sugar,有water。

关键在于Boss 函数中,担任一个整合的职责

同样的Boss函数,我可以通过更换Packing函数,打包方式,获得不同样式的Cola。

通过给getCola函数传入不同想要的参数,获得不同的最终产品。实现了可插拔的函数结构。

装饰者模式

装饰者提供比继承更有弹性的替代方案。 装饰者用用于包装同接口的对象,不仅允许你向方法添加行为,而且还可以将方法设置成原始对象调用(例如装饰者的构造函数)。

装饰者用于通过重载方法的形式添加新功能,该模式可以在被装饰者前面或者后面加上自己的行为以达到特定的目的。

好处:

装饰者是一种实现继承的替代方案。当脚本运行时,在子类中增加行为会影响原有类所有的实例,而装饰者却不然。取而代之的是它能给不同对象各自添加新行为。参考前端手写面试题详细解答

function iwatch () {
    this.battery = 100;
    this.getBattery = function() {
        console.log(this.battery)
    }
}
iwatch.prototype.getNewPart = function(part) {
    this[part].prototype = this; //把this对象上的属性 指向 新对象的prototype
    return new this[part]; //返回一个新对象,不修改原对象,新增了新对象的属性
}
iwatch.prototype.addNetwork = function() {
    this.network = function() {
        console.log('network')
    }
}
iwatch.prototype.addSwim = function() {
    this.swim = function() {
        console.log('swim')
    }
}
var watch = new iwatch();
watch.getBattery(); // 100
watch = watch.getNewPart('addNetwork'); // 添加新行为,network()
watch = watch.getNewPart('addSwim'); // 既有network方法,也有swim方法

在 ES7 中引入了@decorator 修饰器的提案,参考阮一峰的文章。

@testable
class MyTestableClass {
  // ...
}
function testable(target) {
  target.isTestable = true;
}
MyTestableClass.isTestable // true

直接可以使用,装饰器行为

@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;

工厂模式

一个工厂能生产好多不同的产品,最常见的工厂函数就是jQ的$()函数,每一个函数的结果都是一个需要的产品。

function Product(name) {
  this.name = name;
}
Product.prototype.init = function () {
  console.log('init');
}
Product.prototype.go = function () {
  console.log('go');
}
function Factory () {
}
Factory.prototype.add = function(name) {
  return new Product(name);
}
//use
let f = new Factory();
let a = f.add('a');
console.log(a.name);
a.init();
a.go();

适配器模式

Adapter,将一个类(对象)的接口(方法或者属性)转化为另一个接口,以满足用户需求,使类(对象)之间接口的不兼容问题通过适配器得以解决

function Person () {
}
Person.prototype.Say = function() {
  throw new Error("该方法必须被重写!")
}
Person.prototype.Walk = function() {
  throw new Error("该方法必须被重写!")
}
function Dog () {
}
Dog.prototype.Walk = function() {
  throw new Error("该方法必须被重写!")
}
Dog.prototype.shout = function() {
  throw new Error("该方法必须被重写!")
}
function PersonA () {
  Person.apply(this)
}
PersonA.prototype = new Person()
PersonA.prototype.Say = function() {
  console.log('Person say')
}
PersonA.prototype.Walk = function() {
  console.log('Person Walk')
}
function DogBlack () {
  Dog.apply(this)
}
DogBlack.prototype = new Dog()
DogBlack.prototype.Walk = function() {
  console.log('Dog Walk')
}
DogBlack.prototype.shout = function() {
  console.log('Dog Shout')
}
//现在希望Dog类也可以学会Say,并且多走几步
function DogSayAdapter (DogClass) {
  Dog.apply(this)
  this.DogClass = DogClass
}
DogSayAdapter.prototype = new Dog()
DogSayAdapter.prototype.Say = function() {
  this.DogClass.shout()
}
DogSayAdapter.prototype.Walk = function() {
  this.DogClass.Walk()
  this.DogClass.Walk()
}
var personA = new PersonA()
var dogBlack = new DogBlack()
var dogSay = new DogSayAdapter(dogBlack)
personA.Say()
personA.Walk()
dogBlack.Walk()
dogBlack.shout()
dogSay.Say()
dogSay.Walk()//walk * 2    

适配器不只是函数接口,还有数据格式的适配

在前后端数据传递时,常用到适配器模式,也就是通俗易懂的格式化数据,format函数等等

vue的computed计算属性也是适配器模式的一种实现

const originData = [
    {
        title: 'title',
        age: 18,
        content: ['123',321],
        callback: function(){
            console.log(this)
        }
    },
  {
        title: 'title2',
        age: 1,
        content: ['1',3],
        callback: function(){
            console.log('title2')
        }
    }
]
function dataAdapter(data) {
  return data.map(item => {
    return {
      title: item.title,
      content: item.content.join(','),
      init: item.callback
    }
  })
}
var formatData = dataAdapter(originData)

e.g:原始data 的数据不满足当前的要求,通过适配器,把数据格式化成想要的格式,对原始数据没有改变

单例模式

function Simple (name) {
  this.name = name
}
Simple.prototype.go = function() {
  this.name = 'go'
  console.log(this.name)
}
//static静态方法
Simple.getInstance = (function() {
  var ins
  return function(name){
    if (!ins) {
      ins = new Simple(name)
    }
    return ins
  }
})()
let a = Simple.getInstance('a') // name: a
let b = Simple.getInstance('b') // name: a
b===a//true

非单例模式下,相同的new Simple()构造函数,不相等。

通过闭包只创建一次Simple实例,大家公用一个。

惰性单例模式

惰性和懒加载lazyload相似,延迟加载,或者说需要时再加载,不然一次加载过多,频繁进行操作dom影响性能

尽管上述代码有Simple.getInstance方法,可以在需要时再进行实例化,但仍然不是一个好的实现方式。

可以将惰性加载的部分提取出来。

e.g:

var simple = function(fn) {
    var instance;
    return function() {
        return instance || (instance = fn.apply(this, arguments));
    }
};
// 创建遮罩层
var createMask = function(){
    // 创建div元素
    var mask = document.createElement('div');
    // 设置样式
    mask.style.position = 'fixed';
    mask.style.top = '0';
      ...
    ...
    document.body.appendChild(mask);
    // 单击隐藏遮罩层
    mask.onclick = function(){
        this.style.display = 'none';
    }
    return mask;
};
// 创建登陆窗口
var createLogin = function() {
    // 创建div元素
    var login = document.createElement('div');
    // 设置样式
    login.style.position = 'fixed';
    login.style.top = '50%';
      ...
    ...
    login.innerHTML = 'login it';
    document.body.appendChild(login);
    return login;
};
document.getElementById('btn').onclick = function() {
    var oMask = simple(createMask)();
    oMask.style.display = 'block';
    var oLogin = simple(createLogin)();
    oLogin.style.display = 'block';
}

总结

对五种常见常用的设计模式进行了学习,这几种很多时候都会用到,接下来还会继续学习其他的18种设计模式,可能有的设计模式不一定在实际敲码中使用,学了没坏处,总能用得上嗷!

网上对于设计模式的文章,书籍层出不尽,但看得再多,不如自己理解,并且实际使用。很多时候是几种设计模式融合在一起使用,如果不是自己去写一遍,理解一遍,可能常见的设计模式都理解不了。这样就太可惜了,发现干净整洁的代码,都说不出哪里好,就是看着舒服,顺眼,运行速度快…

原文地址:https://blog.csdn.net/helloworld1024fd/article/details/127549851
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • JavaScript翻转字符串方法 js翻转一个字符串
    先把字符串转化成数组String.prototype.split(),再借助数组的reverse方法翻转数组顺序(Array.prototype.reverse()),然后把数组转化成字符串。使用的API方法:String.prototype.split(' ')使用指定的分隔符字符串将一个String对象分割成字符串数组Array.prot
    03-08
  • javascript常见面试题之一:将字符串'get-
    var str='get-element-by-id'; function strToupper(str) { //利用split将字符串分割成数组var arr= str.split('-');for (var i = 1; iarr.length; i++) {      //1.利用for循环获取数组的每个元素,2.用charAt(0)获取每个元素的第一个字符;3.用substr
    03-08
  • JavaScript清除空格、换行,把双引号转换成单引号
    JavaScript清除空格、换行,把双引号转换成单引
    1、页面   2、源码 1 !DOCTYPE2 html3 head4meta charset="utf-8"5 title清除字符串的空格和双引号/title6 style type="text/css"7 textarea{8 padding:10px;9 font-size:18px; 10 width:100%; 11 resize:none; 12 } 13 .main{ 14 padding:40px 10px; 15
    03-08
  • javaScript的Date函数 javascript date(
    1、获取当前时间  Date()获取到的时间是当前设备的显示的时间,开发中要考虑到用户的设备时间是否正确let nowTime = new Date(); // 获取当前时间  把data时间转换成常规格式scriptlet getTimeNow = () = {let nowTime = new Date(); // 获取当前时间——
    03-08
  • JavaScript中什么是闭包
    JavaScript中什么是闭包
    概念:当一个内部函数被调用,就会形成闭包,闭包就是能够读取其他函数内部变量的函数  就是一个函数去访问了另外一个函数的中的变量的函数例子:!DOCTYPE htmlhtmlheadmeta charset="UTF-8"title闭包/title/headbodyscript type="text/javascript"//允许函
    03-08
  • 关于Javascript中通过实例对象修改原型对象属性
    Javascript中的数据值有两大类:基本类型的数据值和引用类型的数据值。基本类型的数据值有5种:null、undefined、number、boolean和string。引用类型的数据值往大的说就1种,即Object类型。往细的说有:Object类型、Array类型、Date类型、Regexp类型、Functio
    03-08
  • javascript中defer的作用(转)
    script src=".js.js" defer/scriptdefer的作用就是作用是文档加载完毕了再执行脚本,这样回避免找不到对象的问题 加上 defer 等于在页面完全在入后再执行,相当于 window.onload ,但应用上比 window.onload 更灵活! defer是脚本程序强大功能中的一个“无名英
    03-08
  • JavaScript Array map() 方法
    JavaScript Array map() 方法
    一、定义map() 方法返回一个新数组,不会改变原始数组。同时新数组中的元素为原始数组元素调用函数处理后的值,并按照原始数组元素顺序依次处理元素。注意:map() 不会对空数组进行检测。二、语法array.map(function(currentValue,index,arr), thisValue)四、
    03-08
  • JavaScript中的arguments,callee,caller(转)
    在提到上述的概念之前,首先想说说javascript中函数的隐含参数:argumentsArguments该对象代表正在执行的函数和调用它的函数的参数。[function.]arguments[n]参数function:选项。当前正在执行的 Function 对象的名字。 n :选项。要传递给 Function 对象的从
    03-08
  • 前台javascript排序 js排序的几种方式
     script type="text/javascript"$(function () {$('.Sorthead-ShowUp').click(function () { var filed = $(this).attr("name"); $(".issorting").removeClass("issorting"); $(this).addClass("issorting"); D
    03-08
点击排行