zoukankan      html  css  js  c++  java
  • JavaScript设计模式基础之闭包(终)

    对于前端程序员来说闭包还是比较难以理解的,

    闭包的形成与变量的作用域以及变量的生产周期密切相关,所以要先弄懂变量的作用域和生存周期。

    1.变量作用域

      变量的作用域,就是指变量的有效范围,通常我们指的作用域就是函数作用域(毕竟全局的作用域没有要指的意义,关键哪都能访问)

      声明变量的时候推荐使用es6语法中的let 和const 可以避免var声明变量出现的一些不必要的错误而且let声明变量只作用于当前作用域 避免使用不带var 或者let直接声明变量,可能会导致命名冲突。

    2.变量生存周期

      除了变量作用域之外,另外一个跟闭包有关的概念就是变量生存周期。

      对于全局变量来说,它的生存周期就是永久,除非我们主动销毁它,而对于函数里面声明的变量来说 它的生存周期会随着函数调用解释而被销毁。

    闭包的定义: 最简单直白的说法就是 函数返回函数

    闭包的应用:封装私有变量、延续局部变量的寿命

    1.封装私有变量:

      使用闭包可以把一些不需要暴露在全局的变量封装成“私有变量”

      如有一个计算数组偶数乘积的方法:

        let num = function(arr){
            let a = 1;
            for(let i = 0; i < arr.length; i++){
                if(arr[i] % 2 === 0){
                    a *= arr[i];
                }
            }
            return a;
        }
       
        console.log( num([1,2,3,4]));//输出8

    加入缓存机制提高函数性能:

    let cache = {};
        let num = function(arr){
            let args = Array.prototype.join.call(arr,',');//输出1,2,3,4
            console.log(cache[args])//第一次调用输出为undefined进行下一步计算 第二次调用输出8 直接返回
            //传入相同参数就比不必进行计算 直接返回缓存提高性能
            if(cache[args]){
                return cache[args];
            }
            //不是相同参数则进行计算
            let a = 1;
            for(let i = 0; i < arr.length; i++){
                if(arr[i] % 2 === 0){
                    a *= arr[i];
                }
            }
            return cache[args] = a;
        }
       
        console.log( num([1,2,3,4]));//8 进行计算
        console.log( num([1,2,3,4]));//8 返回缓存

    这明显能看到cache这个缓存变量只在num函数里面被使用,与其让它们一起暴露在全局不然把它封装在num函数内部,减少页面中的全局变量,以免该变量在其他地方被修改而引发错误

    封装后代码如下:

      

    let num = (function(){
            let cache = {};
            return function(arr){
                let args = Array.prototype.join.call(arr,',');//输出1,2,3,4
                console.log(cache[args])//第一次调用输出为undefined进行下一步计算 第二次调用输出8 直接返回
                //传入相同参数就比不必进行计算 直接返回缓存提高性能
                //判断cache缓存对象里面有args这个key值没
                if(args in cache){
                    return cache[args];
                }
                //不是相同参数则进行计算
                let a = 1;
                for(let i = 0; i < arr.length; i++){
                    if(arr[i] % 2 === 0){
                        a *= arr[i];
                    }
                }
                return cache[args] = a;
            }
           
        })();
       
        console.log( num([1,2,3,4]));//8 进行计算
        console.log( num([1,2,3,4]));//8 返回缓存

    2.延续局部变量寿命

    src属性会自动请求服务器数据如下

      let report = function(src){
                let img = new Image();
                img.src = src;
                console.log(img.src);
            }
            report(`https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1537779456907&di=c72dd79d1dbb02bb9340743bf08e99f7&imgtype=0&src=http%3A%2F%2Fe.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F94cad1c8a786c91723e93522c43d70cf3ac757c6.jpg`);

      但是一些低版本的浏览器实现存在着bug,在这些浏览器上面使用该函数会丢失数据 因为函数调用结束后变量销毁 我们可以用闭包封闭起来就能解决低版本浏览器bug

    代码如下:

      let report = (function(){
                let imgs = [];
                return function(src){
                    let img = new Image();
                    imgs.push(img);
                    img.src = src;
                }
                
            })()
            report(`https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1537779456907&di=c72dd79d1dbb02bb9340743bf08e99f7&imgtype=0&src=http%3A%2F%2Fe.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F94cad1c8a786c91723e93522c43d70cf3ac757c6.jpg`);

    接下来要来点干货了 

    用闭包实现命令模式:

    在JavaScript中闭包的各种设计模式实现里面,闭包的运用特别广泛,在我后续的博客中将体会到这一点

    简单编写一段闭包实现命令模式 如果上述的闭包使用你基本会了的话不会对我们的理解造成困难

    代码如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
    <body>
        <button id="start">打开电脑</button>
        <button id="end">关闭电脑</button>
        <script>
            //命令
            let Computer = {
                open(){
                    alert('打开电脑');
                },
                close(){
                    alert('关闭电脑');
                }
            }
            //创建命令执行中介
            let createCommand = function(receiver){
                //执行
                let execute = function(){
                    return receiver.open();
                }
                //关闭
                let undo = function(){
                    return receiver.close();
                }
                return {
                    execute,
                    undo
                }
            };
            //设置执行命令者
            let setCommand = function(command){
                document.querySelector('#start').onclick = function(){
                    command.execute();//输出打开电脑
                }
                document.querySelector('#end').onclick = function(){
                    command.undo();//输出关闭电脑
                }
            }
            //传入命令方法 传入执行中介 
        setCommand(createCommand(Computer));
        </script>
    </body>
    </html>

    代码还是不难重在理解 

      

  • 相关阅读:
    Sqoop的导入及可能遇到的问题
    Docker搭建MongoDB集群(副本分片)
    微信小程序框架部署:mpvue+typescript
    关系型数据库与非关系型数据库
    PWA 学习笔记(五)
    PWA 学习笔记(四)
    PWA 学习笔记(三)
    PWA学习笔记(二)
    PWA 学习笔记(一)
    部分设计模式对比分析
  • 原文地址:https://www.cnblogs.com/y-y-y-y/p/9695498.html
Copyright © 2011-2022 走看看