小编已经有一段时间没有更新文章了,最近一直在考虑接下来要更新什么内容。接下来,小编会围绕以下三个方面更新文章。实际项目中遇到的问题和解决方案、Vue源码解析、代码重构、关于数据可视化。小编也会按照这个顺序,逐步的去更新。期待着一起进步。

今天就先和大家一起聊一聊我理解的闭包。在聊这个问题之前,先了解一下变量的定义域。

在js中,变量定义域有全局作用域和局部作用域之说。es6中新出现的变量声明关键字,就是为了解决部分变量作用域混乱引入的。全局作用域在这就不谈了。主要说说函数的作用域。

一、作用域

简单一点说,函数的作用域,就是函数的花括号内部,先看两个例子,或许能对这个概念理解的更好一点

function f1(){
let n = 999
console.log(n)
}
f1() // 999 function f2(){
let n = 999
}
alert(n); // 报错

二、函数的返回值

要说闭包之前,我得先说一下函数返回值。关于函数的返回值,小编也是年初才有了一个更深层次的理解。没有返回值的函数,执行之后会返回undefined,有返回值的函数,执行之后就变成了对应的返回值。就像这样

// 没有返回值的函数
function f1(){
alert(666)
}
console.log(f1()) // 出现弹窗之后,在控制台输出undefind // 存在返回值
function f2(){
alert(666)
return 'over'
}
console.log(f2()) // 出现弹窗之后,在控制台输出over。当然,可以返回字符串,也可以返回Bealon,还可以返回函数。

三、函数嵌套
在《重构——改善既有代码的设计》中,提出了js语法是允许函数内部嵌套函数的,但并不是所有的编程语言都可以的,所谓代码嵌套,就是在函数内部又有函数声明,就像这样:

function outer(){
let name = 'lilei'
function inner(){
console.log(name)
}
}

四、闭包
前面说明了在js中的局部变量作用域的问题,在实际项目中,就是需要在函数外部,访问函数内部的变量,这个时候,按照局部变量作用域的问题。似乎是不可能的,闭包的出现,解决了这个问题。

function outer(){
let name = 'lilei'
function inner(){
return name
}
return inner
}

上面是一个典型的闭包函数,在使用这个闭包函数的时候,我们可以这样:

let g = outer()
console.log(g()) // lilei

至此,已经解决了在全局访问函数内的局部变量。但是小编在回家的路上在想,为了实现这个功能,是不是不用这个麻烦,我通过这样的函数,也是可以满足需求的。

function outer(){
let name = 'lilei'
return name
} console.log(outer()) // lilei

确实上面的代码和通过闭包最终在控制台输出的内容是一样的,那为什么还要引入闭包呢?小编也是想了接近一周才明白的,这就好比变量->函数->类,每层往上都是逐步提升的过程,通过函数可以实现更多的逻辑,比如对数据进行处理,仅仅靠变量是不能实现的。

五、闭包的实际应用
上面小编介绍了闭包,那么在实际项目中有什么应用呢?先看下面代码:

1、隐藏内部变量名称和函数执行暂停

function outer() {
let name = 1
function inner() {
return name ++
}
return inner
}
let g = outer()
console.log(g()) // 2
console.log(g()) // 3
console.log(g()) // 4
console.log(g()) // 5

2、setTimeout函数传递参数

默认的setTimeout是这样的

小编也曾经这样试验过

function f1(p) {
console.log(p)
}
setTimeout(f1(666),3000) // 并没有延时,直接输出666

要想通过延时来实现对函数传递参数,这时候,闭包的作用就显现出来了。

function f1(a) {
function f2() {
console.log(a);
}
return f2;
}
var fun = f1(1);
setTimeout(fun,1000); // 一秒之后打印出1

3、回调

定义行为,然后把它关联到某个用户事件上(点击或者按键)。代码通常会作为一个回调(事件触发时调用的函数)绑定到事件。就像下面这块代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试</title>
</head>
<body>
<a href="#" id="size-12">12</a>
<a href="#" id="size-20">20</a>
<a href="#" id="size-30">30</a> <script type="text/javascript">
function changeSize(size){
return function(){
document.body.style.fontSize = size + 'px';
};
} var size12 = changeSize(12);
var size14 = changeSize(20);
var size16 = changeSize(30); document.getElementById('size-12').onclick = size12;
document.getElementById('size-20').onclick = size14;
document.getElementById('size-30').onclick = size16;
</script>
</body>
</html>

4、函数防抖

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

实现的关键就在于setTimeout这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现。就像下面这样:

/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn,delay){
let timer = null //借助闭包
return function() {
if(timer){
clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
timer = setTimeOut(fn,delay)
}else{
timer = setTimeOut(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
}
}
}

六、使用类实现类似闭包中隐藏内部变量功能

上面是一个关于闭包的实际应用,小编在晚上睡不着觉的时候,想起同样的需求,是不是也可以通过类来实现呢?最后经过一顿折腾,答案是肯定的,就像这样:

class Adder{
constructor(c){
this._c = c
}
increace(){
this._c ++
}
decreace(){
this._c --
}
get finalNum(){
return this._c
}
}
let c = new Adder(1)
c.increace()
console.log(c.finalNum) // 2
c.increace()
console.log(c.finalNum) // 3
c.increace()
console.log(c.finalNum) // 4
c.decreace()
console.log(c.finalNum) // 3

参考文章:https://www.cnblogs.com/gg-qq/p/11399152.html

    https://www.cnblogs.com/pikachuworld/p/5325868.html
            https://developer.mozilla.org/zh-CN/docs/Web/API/setTimeout

js中的函数嵌套和闭包的更多相关文章

  1. 聊一下JS中的作用域scope和闭包closure

    聊一下JS中的作用域scope和闭包closure scope和closure是javascript中两个非常关键的概念,前者JS用多了还比较好理解,closure就不一样了.我就被这个概念困扰了很久 ...

  2. JS中给函数参数添加默认值(多看课程)

    JS中给函数参数添加默认值(多看课程) 一.总结 一句话总结:咋函数里面是可以很方便的获取调用函数的参数的,做个判断就好,应该有简便方法,看课程. 二.JS中给函数参数添加默认值 最近在Codewar ...

  3. python基础—函数嵌套与闭包

    python基础-函数嵌套与闭包 1.名称空间与作用域 1 名称空间分为: 1 内置名称空间   内置在解释器中的名称 2 全局名称空间   顶头写的名称 3 局部名称空间 2 找一个名称的查找顺序: ...

  4. JS中的函数节流throttle详解和优化

    JS中的函数节流throttle详解和优化在前端开发中,有时会为页面绑定resize事件,或者为一个页面元素绑定拖拽事件(mousemove),这种事件有一个特点,在一个正常的操作中,有可能在一个短的 ...

  5. 对js中局部变量、全局变量和闭包的理解

    对js中局部变量.全局变量和闭包的理解 局部变量 对于局部变量,js给出的定义是这样的:在 JavaScript函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它.(该变量的作用域 ...

  6. javascript中,一个js中的函数,第一句var _this = this;为什么要这样做?

    javascript中,一个js中的函数,第一句var _this = this;为什么要这样做? 下面是源码: 下面这段代码是常用的网站首页,自动切换span或者tabbar来变更List显示内容的 ...

  7. JS中的函数,Array对象,for-in语句,with语句,自定义对象,Prototype

    一)函数 A)JS中的函数的定义格式: function add(a,b) { var sum = a+b; document.write("两个数的和是:" + sum); // ...

  8. js中的函数,Date对象,Math对象和数组对象

    函数就是完成某个功能的一组语句,js中的函数由关键字 function + 函数名 + 一组参数定义;函数在定义后可以被重复调用,通常将常用的功能写成一个函数,利用函数可以使代码的组织结构更多清晰. ...

  9. js中getByClass()函数

    js中getByClass()函数进化史 对于js来说,我想每一个刚接触它的人都应该会抱怨:为什么没有一个通过class来获取元素的方法.尽管现在高版本的浏览器已经支持getElementsByCla ...

随机推荐

  1. 微服务与SpiringBoot

    微服务: 微服务是一种架构风格,一般说到微服务都会说"微服务架构",即一个系统的各个功能(如结账,用户等)独立出来,以及各个服务独立出来,每个模块是可独立替换.可独立升级的软件单元 ...

  2. Azure AD Domain Service(二)为域服务中的机器配置 Azure File Share 磁盘共享

    一,引言 Azure File Share 是支持两种认证方式的! 1)Active Directory 2)Storage account key 记得上次分析的 "Azure File ...

  3. Hyperledger Fabric 2.x 动态更新智能合约

    一.说明 在上一篇文章中分享了智能合约的安装与使用,如果业务有变更代码需要修改怎么办呢?本文分享如何对已安装的合约进行版本更新. 二.环境准备 区块链网络安装:<Hyperledger Fabr ...

  4. MyBatis辅助功能点三:延迟加载

    延迟加载即先加载必需信息,然后再根据需要进一步加载信息的方式.实际应用如:常出现先查询表A,再根据表A的输出结果查询表B的情况.而有些时候,从A表查询出来的数据,只有一部分要查询表B.这时用延迟加载就 ...

  5. 『无为则无心』Python面向对象 — 45、面向对象编程

    目录 1.面向对象编程的概念 2.面向对象编程和面向过程编程的区别 (1)面向过程编程 (2)面向对象编程 3.举例理解面向对象 4.Python的面向对象编程 5.面向对象的几大核心特性 1.面向对 ...

  6. 部署Nginx网站服务

    编译安装Nginx nginx 官方 : http://nginx.org/ yum -y install pcre-devel zlib-devel   ##安装相关依赖包 ./configure ...

  7. 防世界之Web_warmup

    题目: 啥都没有,右键打开页面源代码查看一下  发现source.php源文件,输入http://111.200.241.244:53776/source.php看看 <?php    high ...

  8. 利用SSH隧道加密技术隐蔽C&C通信流量

    在网络攻防博弈中,网络流量特征分析类安全防御措施得到了广泛应用.众多厂商和企业对网络流量进行恶意流量分析检测,从而针对性的采取防御措施,如各级ISP在骨干网络设备上大多采用网络流量分析检测的防御方案. ...

  9. 在vs2022环境中用C#创建COM组件

    规则 com组件以接口的方式对外开放. 1:所要导出的类必须为公有: 2:所有属性.方法也必须为公有: 3:要导出的属性.方法必须用接口方式: interface IName { [DispId(1) ...

  10. 【CPU】进程管理之五状态模型

    本文为第三篇,进程管理之五状态模型,进程在操作系统里边是有多个状态的,本文就是了解进程在操作系统中的多个状态 1.进程的五个状态 创建状态 就绪状态 阻塞状态 执行状态 终止状态 2.进程处于这五种状 ...