【JS】Day23

学习内容

  • 单例模式
  • 观察者模式
  • 事件轮询机制 (PPT)
  • 垃圾回收机制

单例模式

//设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。

//单个实例,只有一个对象,多次创建,返回同一个对象。
//单例模式的核心:确保只有一个实例,并提供全局访问。

//
<input type="button" value="飞秋" id="btn">
<script>
    /*
        内存管理:
        五个区:
        1. 栈区 : a = 3   由系统自动分配空间,自动释放空间
        2. 堆区:  由程序员分配空间,由程序员释放空间(C语言)
        3. 全局区(静态区):作用范围,整个页面   生存周期:同页面
        4. 常量区 : 3
        5. 代码区
    */

    // var a = 3;
    //面向对象中的静态属性,存放在内存的静态区
    //直接通过构造函数名或类名调用的属性,叫做静态属性

    //一、获取页面元素
    const btn = document.querySelector('#btn');
    //二、创建构造函数
    function FQ(){
        //instance: 实例
        if(!FQ.instance){ //空  instance:实例的意思,是我自己定义的名字。
            FQ.instance = { //对象  instance属性中存放的是一个对象
                //设置属性
                ele: document.createElement('div'),
                //设置方法
                //ES5对象
                //init: function(){}
                //ES6对象简写方式 
                init(){ //init: 初始化的意思,我们自己定义的名字
                    //设置样式
                    this.ele.style.width = '100px';
                    this.ele.style.height = '100px';
                    this.ele.style.background = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
                    document.body.appendChild(this.ele);
                }
            }
        }
        return FQ.instance; //对象
    }
    //三、添加事件
    btn.ondblclick = function(){
        new FQ().init();
    }
</script>

//
<input type="button" value="此电脑" id="btn">
<script>
    //每双一次此电脑,打开一个窗口(单例模式)无论双击多少次,页面中只能出现一个窗口.
    //一、获取页面元素
    const btn = document.querySelector('#btn');
    //二、添加事件
    btn.ondblclick = (function(){
        let div = null;
        return function(){  //闭包函数,也是事件触理函数
            //第一次触发事件时,div中的值为null
            if(!div){
                //如果div中是空的,则创建内容
                //创建一个元素
                div = document.createElement('div');
                //设置属性
                div.style.width = 100 + 'px';
                div.style.height = '100px';
                div.style.background = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
                //添加到页面中
                document.body.appendChild(div);
            }else{ //当前第二次或多次触发事件时,div中已经有上一次创建的对象,将上一次对象换一种颜色即可
                div.style.background = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
            }
        }
    })(); //匿名函数自运行,页面打开时,立即运行。
    //让一个局部变量能够全局访问----闭包
</script>

观察者模式

//观察者模式又叫发布-订阅模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

/*
小明最近看上了一套房子,到了售楼处之后才被告知,该楼盘的房子早已售罄。
好在售楼MM 告诉小明,不久后还有一些尾盘推出,开发商正在办理相关手续,手续办好后便可以购买。
但到底是什么时候,目前还没有人能够知道。
于是小明记下了售楼处的电话,以后每天都会打电话过去询问是不是已经到了购买时间。
除了小明,还有小红、小强、小龙也会每天向售楼处咨询这个问题。
一个星期后,售楼MM 决定辞职,因为厌倦了每天回答 1000 个相同内容的电话。
当然现实中没有这么笨的销售公司,实际上故事是这样的:小明离开之前,把电话号码留在了售楼处。
售楼 MM 答应他,新楼盘一推出就马上发信息通知小明。
小红、小强和小龙也是一样,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼 MM 会翻开花名册,遍历上面的电话号码,依次发送一条短信来通知他们。
——这种发送短信通知就是一个典型的发布-订阅模式(观察者模式)。
小明、小红等购买者都是订阅者,他们订阅了房子开售的消息。
售楼处作为发布者,会在合适的时候遍历花名册上的电话号码,依次给购房者发布消息。
*/
<script>
    //发布-订阅
    //主要角色:发布者
    //1. 有订阅者的联系方式(花名册)
    //2. MM 接待人员 -让你把信息留下
    //3. 发布消息的人员

    //售楼处
    let salesOffice = {
        arr: [], //花名册,用来存储用户的联系方式
        listenUser(fn){
            this.arr.push(fn);
        },
        publishMsg(size,money){
            //遍历数组
            this.arr.forEach(fn => {
                fn(size,money);
            })
        }
    }

    //小明
    salesOffice.listenUser((size,money) => {
        console.log('小明收到消息:' + size + '平方' + money + '元');
    })
    //小龙
    salesOffice.listenUser((size,money) => {
        console.log('小龙收到消息:' + size + '平方' + money + '元');
    })
    //小红
    salesOffice.listenUser((size,money) => {
        console.log('小红收到消息:' + size + '平方' + money + '元');
    })

    //发布消息
    salesOffice.publishMsg(80,600000);
</script>

/*
新冠肺炎疫情期间,小明去药店买口罩,然而到店之后却被店员告知口罩已经售罄。
这时,小明给店员留了一个电话,告诉店员等口罩到货后打电话通知他一下, 这样小明就可以在口罩到货后的第一时间去药店买口罩了。
小明买口罩的过程就是对发布-订阅模式的一次实践:小明将电话留给店员,让店员在口罩到货后通知他,便是一次“订阅”;店员在口罩到货后,打电话告诉小明,便是一次“发布”。
不过,在上面的例子中发布-订阅模式并不是唯一的解决方案,其优势也并没有体现出来。
比如,小明不想把自己的电话留给药店,而是把药店的电话记了下来,每天给药店打电话去问。
从目前的情况看,这种方式也是可以工作的。
但往往我们需要面临更复杂的情况:当前疫情严重,不止小明一人需要口罩,小红、小白、小黑等许许多多的人也都需要口罩,他们也都想在口罩到货的第一时间得到消息,于是他们每个人每天都需要给药店打电话询问。
这样药店每天都会收到几百个电话,店员们每天都被一个简单却重复的问题搞得很疲惫,而小明也每天都担心电话打晚了会被别人先得到消息“抢”走口罩。
当有许多人都想获得“口罩到货”这一相同消息时,发布-订阅模式的优势就显示出来了。
只要所有想知道这个消息的用户都在药店留一个电话,当口罩到货后,店员再给“订阅”这一事件的所有用户都打电话告知一下就可以了。
这样用户就不用每天都给药店打电话询问,药店也不需要每天都花大量的时间接听电话了。
*/
<script>
    //发布者-药店
    let drugstore = {
        //1. 花名册
        roster: {},
        //2. 记录用户信息的方法
        listenUser(eventName,fn){
            //用户需求分类事件
            if(!this.roster[eventName]){
                //不同的需求,都初始化一个空的数组
                this.roster[eventName] = [];
            }
            //将用户的连接方式按分类存入数组
            this.roster[eventName].push(fn);
        },
        //3. 发布信息的方法
        publish(eventName,data){
            //遍历花名册
            //this : 药店对象
            //roster : 属性(对象)
            //eventName : 需要分类 []
            this.roster[eventName].forEach(fn => {
                //fn : 从数组中取出来的第一个用户的联系方式
                fn(data); //data: 是药店传递给用户的信息
            })
        }
    }

    //小明
    function xm(data){
        console.log('口罩到货了,我得赶紧去买点');
    }
    //小龙
    function xl(data){
        if(data.price < 10){
            console.log('真实惠,我得屯点');
        }else if(data.price < 20){
            console.log('还可以,少买点');
        }else{
            console.log('太贵了,我不出门了');
        }
    }
    //小强
    function xq(data){
        if(data.type === '普通医用口罩'){
            console.log('我去买点');
        }else if(data.type === 'N95'){
            console.log('不买了');
        }
    }

    //订阅
    drugstore.listenUser('mask',xm);
    drugstore.listenUser('mask',xl);
    drugstore.listenUser('mask',xq);

    //药店发布的产品信息
    let mask_data = {
        name: '口罩',
        type : 'N95',
        price: 50,
        num: 1000
    }

    //发布消息
    drugstore.publish('mask',mask_data);

    //小红
    function xh(data){
        console.log('赶紧去,就10盒');
    }
    //小花
    function xhua(data){
        console.log('少买点');
    }
    drugstore.listenUser('lianhua',xh);
    drugstore.listenUser('lianhua',xhua);
    //莲花清瘟胶囊 
    let lianhuaqingwen_data = {
        price: 25,
        num: 10
    }
    drugstore.publish('lianhua',lianhuaqingwen_data);
</script>

垃圾回收机制

/*
浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collection),也就是说,执行环境会负责管理代码执行过程中使用的内存。
其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。
但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。
*/
//1. 通常情况下有两种实现方式:**标记清除**和**引用计数**。引用计数不太常用,标记清除较为常用。

//2. js中最常用的垃圾回收方式就是标记清除。
  //目前,IE9+、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。

//3. Netscape Navigator3是最早使用引用计数策略的浏览器,但很快它就遇到一个严重的问题:循环引用。循环引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。
  //在标记清除方式下是没有问题的,但是在引用计数策略下,因为a和b的引用次数不为0,所以不会被垃圾回收器回收[内存](https://so.csdn.net/so/search?q=内存&spm=1001.2101.3001.7020),如果fn函数被大量调用,就会造成内存泄露。在IE7与IE8上,内存直线上升。
  //IE中有一部分对象并不是原生js对象。例如,其中DOM和BOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的js引擎采用标记清除策略来实现,但js访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。
  //要注意的是,IE9+并不存在循环引用导致Dom内存泄露问题,可能是微软做了优化,或者Dom的回收方式已经改变。

//4. 内存管理,什么时候触发垃圾回收?
  //Javascript引擎基础GC方案是(simple GC):mark and sweep(标记清除),即:
    //1. 遍历所有可访问的对象。
    //2. 回收已不可访问的对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值