JS之设计模式

JS之设计模式

## 软件开发设计流程
需求----分析----设计-----编码----测试----部署----运维

## 开发方式
### 瀑布式(假设需求没有变化,不会重复)
需求----分析----设计-----编码----测试----部署----运维
### 迭代式(敏捷)
没有需求或需求完全不明确
不断迭代下面的阶段,需求在不断变化
需求----分析----设计-----编码----测试
导致代码量不断增长----重复的代码-----代码很乱---效率不高-----重构

### 重构
采用设计模式方式重构代码,将冗余的代码精简化

### 做项目,做产品
项目------只需要完成功能
产品------代码很规范,尽量做到最好,最优

## 什么是设计模式?
设计模式---软件开发过程中总结出来最好的代码范式(需要特定场景)
           问题-----红烧鱼块?
           解决办法:----妈妈说了步骤----比较冗余
                    ----百度搜索制作步骤----精练----最简最优
23种设计模式---只需知道常用
单例------
    问题----只需要有一个实例
    解决办法:-----根据经验写---比较冗余
             -----根据单例范式写----最简最优

设计模式---抽象---不好理解

工厂模式

//工厂模式
//问题:需要根据配置文件自动实例化很多对象
//解决:采用工厂模式
function personFactory(name, age, sex) {//定义工厂factory,用来创建对象
    var obj = new Object(); //定义一个空对象
    obj.name = name;
    obj.age = age;
    obj.sex = sex;
    obj.showName = function(){//给对象添加方法
        return this.name;
    }

    return obj; //返回对象
}
//工厂生成对象
var p1 = personFactory("张三","22","男");
var p2 = personFactory("李四","23","男");
console.log(p1.showName())
console.log(p1.constructor)//输出类型---object
//缺点:js里面不清楚p1,p2的构造函数,即不清楚是由哪个函数构造

//构造函数模式
//问题:需要根据配置文件自动实例化很多对象, 如系统启动需要实例很多对象
//解决:采用工厂模式
function PersonFactory(name, age, sex) {
    function Person(name, age, sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.showName = function(){
            return this.name;
        }
        // return this;
    }
    var p = new Person(name, age, sex);
    return p;
}

var p1 = PersonFactory("张三","22","男");
var p2 = PersonFactory("李四","23","男");
console.log(p1.showName())
console.log(p1.constructor) //默认指向自己的构造函数Person
//优点:可以知道实例对象的构造函数

单例模式

在这里插入图片描述

//单例(单一的实例)
//在系统中只能有且只有一个实例, 如资源管理器
//前端如何使用? 如页面上需要多个弹框,需要限制只能有一个(应用场景)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>单例应用</title>
</head>
<body>
    <button id="btn">打开弹框</button>
    <script>
        //创建一个div
        // var createWindow = function(){
        //     var div = document.createElement("div");//在内存中生成一个div
        //     div.innerHTML = "我是弹框,弹框应该唯一"; //给div加内容
        //     div.style.display = "none"; //默认隐藏
        //     document.body.appendChild(div);//把div写入页面
        //     return div;
        // }
        // btn.onclick = function(){
        //     var win = createWindow(); //创建弹框
        //     win.style.display = "block"; //显示弹框
        // }
        //缺点:每一次createWindow()都会在内存中生成一个div副本,没有必要
        //可以使用单例思想重构

        //使用单例模式重构
        var createWindow = (function(){
            var div;
            return function(){ //闭包===函数+外部局部变量(缓存)
                if (!div) {  //若div不存在则创建,否则直接返回
                    div = document.createElement("div");//在内存中生成一个div
                    div.innerHTML = "我是弹框,弹框应该唯一"; //给div加内容
                    div.style.display = "none"; //默认隐藏
                    document.body.appendChild(div);//把div写入页面
                }
                return div;
            }
        })(); //()表示执行
        btn.onclick = function(){
            var win = createWindow(); //创建弹框 //等价于29的function
            win.style.display = "block"; //显示弹框
        }
        
    </script>
</body>
</html>

观察者模式

//观察者---发布订阅
//场景----买饭(跟服务员说----买XXX饭----服务员下单(订阅)-----服务员通知(发布)-----)
//观察者----服务员
//被观察者-----订单

var releaseObj = {}; //发布者 观察者
releaseObj.list = [];  //订单列表
//添加订单行为
releaseObj.listen = function(fn) { //fn----订单
    releaseObj.list.push(fn);//把订单存入订单列表
}
//客户通知服务员下单----添加到list列表里面
releaseObj.listen(function(name, price){ //订单
    //若时间太长,取消订单(直接把list中的函数去掉)
    console.log("红烧肉饭订单:name:", name, ",price:", price, this.list);
})
//等待-----玩手机-----等待服务员的通知
releaseObj.notify = function(){ //通知订单ok
    for(var i=0, fn; fn=this.list[i++];) {//var i=0; i<xxx; i++
        //arguments---会接收所有实参   
        //执行函数 call----参数,参数  apply---[参数,参数]
        //fn.apply---表示执行fn
        //fn(this, arguments) fn---function(name, price)
        //目的:执行fn 保证fn在this作用域里,将所有实参传入fn
        fn.apply(this, arguments); //this---releaseObj  arguments---参数
        // fn.call(this, "红烧肉饭", "20");
    }
}
releaseObj.notify("红烧肉饭", "20"); //通知饭已Ok

模板模式

模板----
    模具----已经存的一种框架,实物,电子档
    饼干----模具
    定义一个抽象的对象(bike),有一个固定的方法(如滚动)
    ----根据这个抽象对象去实现子类(捷安特)
    ----也须实现抽象方法(如滚动)
//模板模式----用于构建统一的对象----所有对象拥有统一的行为
//定义抽象自行车----不存在----约定
//老曹----继承财产-----把国籍转为中国
//约定----要创建一子类bike实现必须实现wheel方法和hdrive方法
var Bike = function(){ //模板对象
}  //空对象
Bike.prototype.wheel = function(){ //空对象定义轮子
    throw new Error("抽象轮子方法");
}
Bike.prototype.hdrive = function(){ //空对象定义动力系统
    throw new Error("抽象动力系统");//避免子类不实现该方法,直接调用报错
}
// 不能这样写  因为调方法就直接报错
// var bike = new Bike();
// bike.hdrive()

//电动车模板
var EBike = function(){}
EBike.prototype.wheel = function(){ //空对象定义轮子
    throw new Error("抽象轮子方法");
}
EBike.prototype.edrive = function(){ //空对象定义动力系统
    throw new Error("抽象动力系统");//避免子类不实现该方法,直接调用报错
}

//根据模板 造子类捷安特
var JATBike = function(){
    Bike.call(this);  //把bike的属性拷贝过来
}
JATBike.prototype = new Bike();
JATBike.prototype.wheel = function(){
    //业务逻辑
    console.log("捷安特轮子");
}
JATBike.prototype.hdrive = function(){
    //业务逻辑
    console.log("人力驱动");
}
var jatBike = new JATBike();
jatBike.hdrive();

//根据电动车模板---造子类雅迪
var YdBike = function(){
    EBike.call(this);
}
YdBike.prototype = new EBike();
YdBike.prototype.wheel = function(){
    //业务逻辑
    console.log("捷安特轮子");
}
YdBike.prototype.edrive = function(){
    //业务逻辑
    console.log("电力驱动");
}

适配器模式

适配器---
    手机充电---需要2孔
    电脑充电---需要3----买插座----墙上2孔
    插座---2----3孔
    问题:
    有了自行车,电动车----自行车太累了,电动车买不起-----改装自行车用二手电动车马达
/adapter----适配器-----源码
//改装自行车
var BikeAdapter = function(engine){
    Bike.call(this);
    this.engine = engine; //马达
}
BikeAdapter.prototype = new Bike();
BikeAdapter.prototype.wheel = function(){
    //业务逻辑
    console.log("捷安特轮子");
}
BikeAdapter.prototype.hdrive = function(){
    //业务逻辑
    console.log("马达驱动", this.engine.edrive()); //不再需要人力驱动
}

var ydBike = new YdBike();
var bikeAdapter = new BikeAdapter(ydBike);
bikeAdapter.hdrive(); //改装后的自行车

代理模式

代理----设计模式
    别人代理你的行为,如明星---经理人----行为必须一致,经理人行事必须是按明星要求来
    webpack--proxy(代理)---将请求--直接过不去--需要代理转发
//代理---代理对象要与被代理对象有相同行为
//男----送礼物-----委托代理送
var girl = function(name) {
    this.name = name;
}
var boy = function(girl) {
    this.girl = girl;
    //送礼物
    this.sendGift = function(gift) {
        //业务逻辑
        console.log("hi "+girl.name+ ", 有一个男孩送你礼物:"+gift);
    }
}
//代理对象---具备了送礼物能力
var proxyObj = function(girl) {
    this.girl = girl;
    //送礼物
    this.sendGift2 = function(gift) {
        //new实例化boy对象
        //var boy = new boy(this.girl);
        //boy.sendGift(gift);
        (new boy(this.girl)).sendGift(gift);  //送礼物 用了男孩子的方法
    }
}
//调用过程----只知道是代理对象所为
var proxy = new proxyObj(new girl("小花"));
proxy.sendGift2("99朵玫瑰");

例子:前端加载图片(真实图片–很大, 等待图片—很小—代理)
请看proxy.html (见下)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>代理应用</title>
</head>
<body>
    <script>
        //创建一个图片对象-----冗余----耦合度高
        /*var myImage = (function(){
            //imgNode---是用来显示图片的
            var imgNode = document.createElement("img"); //创建一个图片
            document.body.appendChild(imgNode);  //图片添加到body
            //img ---- 不用来显示,只用来加载, 加载完成就替换
            var img = new Image();
            img.onload = function(){//当真实图片下载完成时触发
                setTimeout(()=>{
                    imgNode.src = this.src;//用真实图片替换临时图片 this----img
                }, 1000);
            }
            return {
                setSrc: function(src) {
                    imgNode.src = "http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif"; //临时图片
                    img.src = src; //真实图片,当赋值后img会发送请求去下载真实图片
                }
            }
        })();
        //设置真实图片
        myImage.setSrc("https://www.baidu.com/img/bd_logo1.png"); //真实图片*/


        //使用代理模式重构,把imgNode,img分开
        //imgNode---显示
        var myImage = (function(){
            var imgNode = document.createElement("img"); //创建一个图片
            document.body.appendChild(imgNode);  //图片添加到body
            return {
                setSrc: function(src){  //变量
                    imgNode.src = src; //用来接收图片地址
                }
            }
        })();
        //图片代理
        //var img = new Image();----空img---内存
        //img.src = src; 给img赋,一旦赋值,它就会发送http请求下载图片
        //img.onload ---下载完成就会触发该事件
        var ProxyImage = (function(){ //ProxyImage就是return的结果
            var img = new Image();
            img.onload = function(){//当真实图片下载完成时触发 onload onclick
                setTimeout(()=>{
                    myImage.setSrc(this.src);//用真实图片替换临时图片 this----img  this----全局(闭包img)
                }, 1000);
            }
            return {
                setSrc2: function(src) {
                    myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif"); //临时图片
                    img.src = src; //真实图片,当赋值后img会发送请求去下载真实图片
                }
            }
        })();
        ProxyImage.setSrc2("https://www.baidu.com/img/bd_logo1.png"); //真实图片
        //0.先生成myImage, ProxyImage
        //1.ProxyImage.setSrc设置真实的图片
        //2.ProxyImage.setSrc设置真实图片同时指定临时图片
        //3.临时图片会被立即添加到页面并显示
        //4.img.src = src真实图片同时也在加载中。。。
        //5.若真实图片加载完毕则通过myImage.setSrc替换临时图片
        //return ----- 返回 ---需要返回值就需要return
    </script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值