聊聊闭包和装饰器

在C/C++一类语言里好像没有看到闭包和装饰器的概念,在学习Python、JavaScript、TypeScript时,闭包和装饰器好像是一条迈不过去的坎,有必要深入理解一下这两个语言概念。

JavaScript是面向过程的语言,不是面向对象的语言,其核心语法是函数。其虽然没有面向对象的语法,但其将函数function玩得溜溜的,在function函数内还套用函数,这种做法已十分接近面向对象的语法了,只差把function改为class了。

JavaScript函数内的函数叫闭包,C++内里的函数叫方法,对比来理解就很好懂。——我是学C++的,转过来学JavaScript,也就这么对比来理解了。

查看他人的文章,了解JavaScript引入闭包,是为了解决变量作用域的问题。为了后续表述方便,及表述完整,后面把闭包叫做闭包函数,并将其上层函数叫做闭包父函数。——闭包和装饰器都是函数,完整地叫闭包函数和装饰器函数,有助于理解概念。

看如下的代码,可以完整理解闭包函数的概念:

    <script>
        function fatherFunc() {

            console.log("闭包父函数执行......")

            // 闭包父函数中定义了一个局部变量
            var localVar = 1;

            function closureFunc() {

                // 在闭包函数内可以读闭包父函数中的局部变量
                console.log("闭包函数开始时的输出结果:" + localVar)

                // 在闭包函数内还可以改变闭包父函数中的局部变量
                localVar = localVar + 1;
                console.log("闭包函数结束时的输出结果:" + localVar);
            }
            return closureFunc;
        }


        // 1. 调用闭包父函数
        var obj = fatherFunc();

        // 2. 第一次调用闭包函数
        obj();

        // 3. 第二次调用闭包函数
        obj();

    </script>

// 执行结果:
闭包父函数执行......

闭包函数开始时的输出结果:1 
闭包函数结束时的输出结果:2 

闭包函数开始时的输出结果:2
闭包函数结束时的输出结果:3

现在来慢慢解读这段代码,有5点关键内容需要完全掌握,缺一不可:

  1. 定义了一个闭包父函数fatherFunc()。
  2. 在闭包父函数内部定义了一个局部变量localVar,并赋值为1。
  3. 在闭包父函数内还定义了一个闭包函数closureFunc()。
  4. 在闭包函数内使用了闭包父函数的局部变量localVar,做了加1操作。
  5. 闭包父函数返回了闭包函数(指针或地址)。——JavaScript语言中没有指针和地址的概念,这里引用C++的说法,比较容易理解。实质就是函数的内存首地址。

现在来看函数调用:

  1. 调用闭包父函数:从执行结果看,闭包父函数内的闭包函数没有执行。
  2. 闭包父函数执行后的结果赋值给了变量obj。从代码看,这个obj是闭包函数的内存首地址。
  3. 第一次调用闭包函数:闭包父函数的局部变量localVar由1变为2。
  4. 第二次调用闭包函数:闭包父函数的局部变量localVar由2变为3。

可以预测,持续调用闭包函数,闭包父函数的局部变量会持续增加,实际也是这样。

这就是这段代码的奇特之处,一个函数内的局部变量,闭包函数可以反复访问,其值会持续保持上次操作的结果。这和我们通常理解的局部变量有很大不同,局部变量在函数退出后(闭包父函数执行结束后)即消失,也就是作用域消失。现在的实际情况是,这个局部变量好像被一个什么东东包起来了、保护起来了,没有消失。这个“包包”就是闭包!闭包包什么?包的就是局部变量,使其作用域持续保持。

如果你是C++/Java程序员,把闭包父函数理解为类Class,把闭包父函数的局部变量理解为类的属性,把闭包函数理解为类的方法,一切都顺理成章了。

下面来分析装饰器这个概念。装饰器是一个函数,下面对其的完整称呼为装饰器函数。

基于闭包函数,理解装饰器函数就比较容易了。这里使用三个函数,分别是原函数闭包函数/打包函数装饰器函数/闭包父函数。装饰器函数就是闭包父函数。

<script>
       function decoratorFunc(srcFunc){
            var localVar = srcFunc;
            console.log("我是装饰器函数,对原函数进行装饰。其实我啥也不干,干活的是我的小弟闭包函数。");
            function closureFunc(){
                console.log("我就是装饰器函数的闭包函数,对原函数的装饰累活、脏话都由我来干,哎!");
                console.log("调用原函数前做一些事情......");
                srcFunc();
                console.log("......调用原函数后再做一些事情");
            }
            return closureFunc;
        }

        function srcFunc(){
            console.log("我是原函数");
        }

        var obj = decoratorFunc(srcFunc);
        obj()

    </script>

//执行结果:
我是装饰器函数,对原函数进行装饰。其实我啥也不干,干活的是我的小弟闭包函数。 

我就是装饰器函数的闭包函数,对原函数的装饰累活、脏话都由我来干,哎! 
调用原函数前做一些事情...... 
我是原函数 
......调用原函数后再做一些事情

代码解读:

  1. 定义一个装饰器函数decoratorFunc()。
  2. 装饰器函数的输入参数为原函数srcFunc的内存首地址。——要理解为:原函数的内存首地址是装饰器函数的局部变量。
  3. 装饰器函数内定义了一个闭包函数closureFunc()。
  4. 闭包函数内部使用了装饰器函数的局部变量srcFunc。
  5. 装饰器函数返回了闭包函数的内存首地址。
  6. 定义了一个原函数srcFunc()。
比较装饰器函数 – 打包函数 – 原函数闭包父函数 – 闭包函数 – 闭包父函数局部变量
1定义一个装饰器函数decoratorFunc()。定义了一个闭包父函数fatherFunc()。
2装饰器函数的输入参数为原函数srcFunc的内存首地址。——要理解为:原函数的内存首地址是装饰器函数的局部变量。在闭包父函数内部定义了一个局部变量localVar,并赋值为1。
3装饰器函数内定义了一个闭包函数closureFunc()。在闭包父函数内还定义了一个闭包函数closureFunc()。
4闭包函数内部使用了装饰器函数的局部变量srcFunc。在闭包函数内使用了闭包父函数的局部变量localVar,做了加1操作。
5装饰器函数返回了闭包函数的内存首地址。闭包父函数返回了闭包函数(指针或地址)。——JavaScript语言中没有指针和地址的概念,这里引用C++的说法,比较容易理解。实质就是函数的内存首地址。
6定义了一个原函数srcFunc()。

把装饰器函数和闭包函数放在一起来看,两者既形似、也神似,只有两点稍有区别:

  1. 闭包父函数的局部变量变成了装饰器函数的输入参数。
  2. 输入参数是外部的一个原函数的内存首地址。

闭包函数的作用是解决局部变量的作用域问题,装饰器函数的作用是什么呢?与闭包函数类似,其作用是对原函数进行装饰。要这样来理解装饰:——与装饰房间有所不同。

  1. 保持原函数的纯正,不要在原函数内增加代码。——原函数会被很多地方调用,不能增加无用的华丽装饰。装饰器的真正目的不是为了装饰,而是保持原函数的纯正。真让人意外!这就是辩证法。
  2. 在某些场合,在原函数的基础上增加点操作,增加点“装饰”。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值