普通视图
-
LiuShen's Blog
-
魔改笔记七:分类条及外链卡片
虽然说域名换了几乎等于从头开始,但是仍然浇不灭我对于网站的热情,该进行的还是得进行。很久都没有总结魔改了,这篇文章就将最近魔改最大的几个部分进行记录,同时对有需要的朋友提供帮助。
-
LiuShen's Blog
-
魔改笔记六:twikoo及导航栏美化
近期群里有小伙伴问我我的顶栏是怎么实现的动效,还有评论区也在问我twikoo的美化是怎么实现的,于是我就写一篇文章一起水一下好啦!
魔改笔记六:twikoo及导航栏美化
-
LiuShen's Blog
-
twikoo仿段落评论,实现快速评论功能
先前twikoo的讨论群中提出了一个功能,类似于小说之类的段落评论功能,我思考了一下,感觉这个功能很有意思,评论起来比评论区快多了,于是用我为数不多的JS知识,尝试实现了一下,没想到还真让我给搞出来了。
twikoo仿段落评论,实现快速评论功能
-
LiuShen's Blog
-
魔改笔记五:从头开始,手搓一个关于页面
我的关于页面一直沿用的是原本的butterfly的一套,只有干巴巴的文字,没有任何新奇的地方,我自己都懒得看,网站也慢慢趋于稳定,这些屎山也得慢慢修起来了,于是我从头开始,手搓了一个关于页面
魔改笔记五:从头开始,手搓一个关于页面
-
LiuShen's Blog
-
魔改笔记四:友链页重构及友链朋友圈适配
前天晚上,店长更新了方舟系列的友链卡片,作为店长铁粉,当然要第一时间买店长的火柴啦,于是我将店长所有的友链卡片稍微整合了一下,结合安知鱼的教程,最终实现了“五世同堂”,五种友链可以混合搭配,于是在此分享给大家!
魔改笔记四:友链页重构及友链朋友圈适配
-
BeaCox
-
谈谈闭包
在学习JS的过程中,我遇到了闭包这个概念,当时并没有在意。直到最近我开始自学python,在廖雪峰老师的python教程中又一次看到了这个名词,我才意识到闭包其实是一个重要的概念,或者说特性,许多高级语言支持闭包(比如近些年比较火的Go语言)。于是我查看了相关文档、教程,打算谈谈我对闭包的一些认识。闭包的定义闭包有许多不同的定义,个人认为最简洁而达意的是MDN对于闭包的定义:闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。词法环境维基百科这样描述闭包中的词法环境:环境里是若干对符号和值的对应关系,它既要包括约束变量(该函数内部绑定的符号),也要包括自由变量(在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。简单来说,词法环境包含两部分:环境记录:存储符号-值对对外部环境的引用:对父级词法环境的引用。也就是说,一个函数的词法环境包含了在函数中的符号定义和函数外部的词法环境。考虑如下python代码:1234567def init(): na
谈谈闭包
在学习JS的过程中,我遇到了闭包这个概念,当时并没有在意。直到最近我开始自学python,在廖雪峰老师的python教程中又一次看到了这个名词,我才意识到闭包其实是一个重要的概念,或者说特性,许多高级语言支持闭包(比如近些年比较火的Go语言)。于是我查看了相关文档、教程,打算谈谈我对闭包的一些认识。
闭包的定义
闭包有许多不同的定义,个人认为最简洁而达意的是MDN对于闭包的定义:
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。
词法环境
维基百科这样描述闭包中的词法环境:
环境里是若干对符号和值的对应关系,它既要包括约束变量(该函数内部绑定的符号),也要包括自由变量(在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。
简单来说,词法环境包含两部分:
- 环境记录:存储符号-值对
- 对外部环境的引用:对父级词法环境的引用。
也就是说,一个函数的词法环境包含了在函数中的符号定义和函数外部的词法环境。考虑如下python代码:
1 | def init(): |
displayName
函数的词法环境包含了环境记录(greeting
的符号-值对)以及对外部环境的引用(name
和displayName
的符号-值对),这也就是在displayName
函数中可以访问name
变量的原因。执行displayName
函数,其实就是创建了一个闭包。
使用闭包
看完上面的例子,好像有点迷糊了:这不就是“内层作用域可以访问外层作用域的变量”吗?C++不支持闭包,不也能完成上面的工作吗?这是因为上面的例子并没有展示出闭包函数与词法环境捆绑的特性。将上面的代码稍加改动:
1 | def init(): |
这段代码与上面不同的地方在于,displayName
函数并不在init
函数中执行,而是作为返回值,在init
函数外部,有一个outsideDisplay
接收了这个返回值。
如果我们从C++的思想来考虑这段代码,会发现:在init
函数执行完后,局部变量name
已经被回收,这时候outsideDisplay
中name
变量是没有被定义的,这段代码应该不能正常运行。
然而,我们运行这段python程序后会发现,终端正常输出Hello, BeaCox
,这就是闭包的魔力!
这段程序之所以正常运行的原因,就是python
中返回函数会形成闭包。闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。在本例子中,outsideDisplay
是执行 init
时创建的 displayName
函数实例的引用。displayName
函数和其捆绑的词法环境(变量 name
存在于其中)的引用形成了一个闭包,因此init
函数执行完毕后,该词法环境没有消失,变量name
也没有被回收。因此,当 outsideDisplay
被调用时,变量 name
仍然可用,程序能够正确运行。
闭包的用途
模拟公有成员函数对私有变量的操作
读完上述代码不难发现,outsideDisplay
函数在init
函数外部调用,但却访问到了init
函数内部的变量。这与C++中,调用类的公有成员函数来操作类的私有变量非常相似。与C++不同,python不存在严格意义上的私有变量,python通过以双下划线为开头来命名变量的方式,实现的是一种伪私有变量,它不应该被从外部访问,而不是不能被从外部访问。python、JavaScript等不支持严格私有变量的语言可以通过创建闭包来模拟公有成员函数对私有变量的操作
创建一个生命周期极长的局部变量
观察上述例子,outsideDisplay
函数可以继续重复运行,直到整个程序终止。也就是说name
变量直到程序运行结束之前,都一直存在于内存中。听起来貌似很像全局变量,但这个变量却是一个局部变量。仅这个程序而言,这个变量只能被outsideDisplay
函数和init
函数访问。
闭包可能导致的问题
内存泄漏
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
上文提到,闭包可以创建一个生命周期极长(直到程序运行结束前始终留存在内存中)的变量,如果这样的变量过多,就会导致程序运行速度减慢甚至系统崩溃。
在循环中创建闭包导致意料之外的错误
廖雪峰老师的python教程中给出了一个这样的例子:
1 | def count(): |
这段程序的期望目标是f1, f2, f3分别返回1,4,9。实际返回9, 9, 9。这是因为每个f()
函数捆绑外部词法环境中的i
是对i
的引用,在return fs
之前,i
已经变成3了,因此每个f()
函数返回的都是3*3
。
因此,要尽量避免在循环中创建闭包。如若必需,务必要谨慎!
-
BeaCox
-
基于GitHub Actions的看雪论坛自动签到,可选推送与否
看雪论坛称得上是国内较好的安全论坛了。不过要1k雪币(论坛虚拟币,新用户几乎都可以获得220及以上)才可以升级为正式会员。临时会员有诸多限制,包括不能查看『WEB安全』版块等。对于我这种想白嫖的安全小白来说,唯一的方法就是每天签到随机获得1-10枚雪币。但是我经常会忘记签到,这等到猴年马月?正好我最近正在学习JS,于是写了一个自动签到的脚本。当然,除了升级正式会员,雪币还有许多用处,所以对已经是正式会员的用户来说也还算有些用罢。先上传送门:pediy-CheckInhttps://github.com/BeaCox/pediy-CheckIn实现方法这个脚本的实现非常简单。通过抓包可以发现,看雪论坛的签到是通过向https://bbs.pediy.com/user-signin.htm页面发送含Cookie的POST请求来实现的(也是绝大多数签到业务的设计逻辑),因此利用Axios库的API来向该页面发送请求,模拟用户签到。签到完成后,将响应的数据赋值给一个对象,通过response.data.code和response.data.message来判断网络正常情况下,签到任务的三种可能
基于GitHub Actions的看雪论坛自动签到,可选推送与否
看雪论坛称得上是国内较好的安全论坛了。不过要1k雪币(论坛虚拟币,新用户几乎都可以获得220及以上)才可以升级为正式会员。临时会员有诸多限制,包括不能查看『WEB安全』版块等。对于我这种想白嫖的安全小白来说,唯一的方法就是每天签到随机获得1-10枚雪币。但是我经常会忘记签到,这等到猴年马月?
正好我最近正在学习JS,于是写了一个自动签到的脚本。当然,除了升级正式会员,雪币还有许多用处,所以对已经是正式会员的用户来说也还算有些用罢。
先上传送门:
实现方法
这个脚本的实现非常简单。
- 通过抓包可以发现,看雪论坛的签到是通过向
https://bbs.pediy.com/user-signin.htm
页面发送含Cookie的POST请求来实现的(也是绝大多数签到业务的设计逻辑),因此利用Axios库的API来向该页面发送请求,模拟用户签到。 - 签到完成后,将响应的数据赋值给一个对象,通过
response.data.code
和response.data.message
来判断网络正常情况下,签到任务的三种可能情况。code == 0 && message = <签到获得雪币数>
: 表示签到成功。推送消息显示`签到成功,获得${msg}雪币`。code == -1 && message == '您今日已签到成功'
: 表示已经签到过,此处为重复签到。推送消息显示’您今日已签到成功’。code == -1 && message == '请先登录'
: 表示Cookie验证失败。打印错误并不推送消息
- 推送消息的功能利用pushplus提供的接口实现,因为比Server酱免费版限制少一些,当然后续可能会添加server酱等其他选项。同样是利用了Axios的库来向接口发送请求。可以参考pushplus文档中心
- 利用GitHub Actions,在GitHub提供的主机上用node运行js,通过
crontab
完成定时任务。 - GitHub Actions在仓库60天以上没有任何活动时会被suspended(推迟),因此利用Keepalive Workflow来使工作流按期运行。
后续
希望各位能帮我点一个star✨(理直气壮)
由于这是我第一个js脚本,程序健壮性想必不甚好,欢迎大家提出issue和pr!