js中的闭包实例展示


Posted in Javascript onNovember 01, 2018

前言

准确来说,闭包是基于正常的垃圾回收处理机制下的。也就是说,一般情况一个函数(函数作用域)执行完毕,里面声明的变量会全部释放,被垃圾回收器回收。但闭包利用一个技巧,让作用域里面的变量,在函数执行完之后依旧保存没有被垃圾回收处理掉。

闭包

定义

MDN定义

javascriptkit

词法作用域

闭包的三大特点为:

1、函数嵌套函数

2、内部函数可以访问外部函数的变量

3、参数和变量不会被回收。

作用域链
函数在执行的过程中,先从自己内部找变量如果找不到,再从创建当前函数所在的作用域(词法作用域)去找, 以 此往上注意找的是变量的当前的状态

作用域链的博客

函数连同它作用域链上的要找的这个变量,共同构成闭包

一般情况下使用闭包主要是为了

  • 封装数据
  • 暂存数据

一个典型的闭包案例

function car(){
 var speed = 0
 function fn(){
 speed++
 console.log(speed)
 }
 return fn
}
var speedUp = car()
speedUp() //1
speedUp() //2

当函数内部没有执行以下的代码时

function fn(){
 speed++
 console.log(speed)
 }
return fn

在代码执行完成后,函数内部的局部变量speed就会被销毁,由于全局标量speedUp一直存在(除非关闭当前页面,否则全局变量一直存在),那么函数内部的作用域就没有办法被销毁,里面有东西一直被使用,这点与浏览器的垃圾回收机制相仿,当我们执行speedUp(),他会在函数的词法作用域下去寻找,函数里面又返回了一个fn,因而形成闭包,简单的理解为

var speed = 0
function fn(){
 speed++
 console.log(speed)
}

这一段代码形成一个闭包,如果不return fn,那函数内部的局部变量就会被销毁。

我们可以看看上述代码利用立即执行语句和立即执行函数可以怎么演变:

function car(){
 var speed = 0
 function fn(){
 speed++
 console.log(speed)
 }
 return fn
}
var speedUp = car()
//1
function car(){
 var speed = 0
 return function (){
 speed++
 console.log(speed)
 }
}
var speedUp = car()
//2
function car(speed){
 return function (){
 speed++
 console.log(speed)
 }
}
var speedUp = car(3)
//3
function car(){
 var speed = arguments[0]
 return function (){
 speed++
 console.log(speed)
 }
}
var speedUp = car()
//4
function car(){
 var speed = 0
 return function (){
 speed++
 console.log(speed)
 }
}
//5 car可以不写,则为匿名函数 
var speedUp = (function car(speed){
 return function (){
 speed++
 console.log(speed)
 }
}
)(3)

闭包的相关案例

如下代码输出多少?如果想输出3,那如何改造代码?

var fnArr = [];
for (var i = 0; i < 10; i ++) {
 fnArr[i] = function(){
 return i
 };
}
console.log( fnArr[3]() ) // 10

同等演变

假设只有两层循环:

var fnArr = []
for (var i = 0; i < 2; i ++) {
 fnArr[i] = (function(j){
 return function(){
 return j
 } 
 })(i)
}
fnArr[3]()
//1
var fnArr = [] 
fnArr[0] = (function(j){
 return function(){
 return j
 } 
 })(0)
}
fnArr[1] = (function(j){
 return function(){
 return j
 } 
 })(1)
}
fnArr[3]()
//2
var a = (function(j){
 return function(){
 return j
 } 
 })(0)
}
var b = (function(j){
 return function(){
 return j
 } 
 })(1)
}
b()
//3
var a = (function(j){
 return function(){
 return j
 } 
 })(0)
}
function fn2(j){
 return function(){
 return j
 }
}
var b = fn2(1)
//4
var a = (function(j){
 return function(){
 return j
 } 
 })(0)
}
function fn2(j){
 return function(){
 return j
 }
 return f
}
var b = fn2(1)
//5
var a = (function(j){
 return function(){
 return j
 } 
 })(0)
}
function fn2(j){
 var j = arguments[0]
 function f(){
 return j
 }
 return f
}
var b = fn2(1)

改造后(立即执行语句,演变过程)

var fnArr = []
for (var i = 0; i < 10; i ++) {
 fnArr[i] = (function(j){
 return function(){
 return j
 } 
 })(i)
}
console.log( fnArr[3]() ) // 3
var fnArr = []
for (var i = 0; i < 10; i ++) {
 (function(i){
 fnArr[i] = function(){
 return i
 } 
 })(i)
}
console.log( fnArr[3]() ) // 3
var fnArr = []
for (let i = 0; i < 10; i ++) {
 fnArr[i] = function(){
 return i
 } 
}
console.log( fnArr[3]() ) // 3

封装一个 Car 对象

var Car = (function(){
 var speed = 0;
 function set(s){
 speed = s
 }
 function get(){
 return speed
 }
 function speedUp(){
 speed++
 }
 function speedDown(){
 speed--
 }
 return {
 setSpeed: setSpeed,
 get: get,
 speedUp: speedUp,
 speedDown: speedDown
 }
})()
Car.set(30)
Car.get() //30
Car.speedUp()
Car.get() //31
Car.speedDown()
Car.get() //3

如下代码输出多少?如何连续输出 0,1,2,3,4

for(var i=0; i<5; i++){
 setTimeout(function(){
 console.log('delayer:' + i )
 }, 0)
}

输出结果为:delayer:5(连续输出5个),执行setTimeout时,代码会挂到任务队列中区,待i遍历完成之后执行,而此时i = 5,所以输出delayer:5(连续输出5个)

修改后

for(var i=0; i<5; i++){
 (function(j){
 setTimeout(function(){
 console.log('delayer:' + j )
 }, 0)//1000-1000*j 
 })(i)
}

或者

for(var i=0; i<5; i++){
 setTimeout((function(j){
 return function(){
 console.log('delayer:' + j )
 }
 }(i)), 0) 
}

如下代码输出多少?

function makeCounter() {
 var count = 0
 return function() {
 return count++
 };
}
var counter = makeCounter()
var counter2 = makeCounter();
console.log( counter() ) // 0
console.log( counter() ) // 1
console.log( counter2() ) // 0
console.log( counter2() ) // 1

补全代码,实现数组按姓名、年纪、任意字段排序

var users = [
 { name: "John", age: 20, company: "Baidu" },
 { name: "Pete", age: 18, company: "Alibaba" },
 { name: "Ann", age: 19, company: "Tecent" }
]
users.sort(byName) 
users.sort(byAge)
users.sort(byField('company'))

解答

function byName(user1, user2){
 return user1.name > user2.name
}
function byAge (user1, user2){
 return user1.age > user2.age
}
function byFeild(field){
 return function(user1, user2){
 return user1[field] > user2[field]
 }
}
users.sort(byField('company'))

写一个 sum 函数,实现如下调用方式

console.log( sum(1)(2) ) // 3
console.log( sum(5)(-1) ) // 4

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
懒就要懒到底——鼠标自动点击(含时间判断)
Feb 20 Javascript
Firefox outerHTML实现代码
Jun 04 Javascript
各浏览器对link标签onload/onreadystatechange事件支持的差异分析
Apr 27 Javascript
setInterval计时器不准的问题解决方法
May 08 Javascript
JavaScript对象之深度克隆介绍
Dec 08 Javascript
浅谈javascript中基本包装类型
Jun 03 Javascript
mac上配置Android环境变量的方法
Jul 08 Javascript
js中的数组对象排序分析
Dec 11 Javascript
jQuery实现的自定义轮播图功能详解
Dec 28 jQuery
vue动态绘制四分之三圆环图效果
Sep 03 Javascript
Vue 自定义指令实现一键 Copy功能
Sep 16 Javascript
微信小程序自定义组件components(代码详解)
Oct 21 Javascript
微信小程序实现登录遮罩效果
Nov 01 #Javascript
在vue里使用codemirror遇到的问题
Nov 01 #Javascript
vue中使用codemirror的实例详解
Nov 01 #Javascript
vue-lazyload使用总结(推荐)
Nov 01 #Javascript
vue 中基于html5 drag drap的拖放效果案例分析
Nov 01 #Javascript
Vue列表渲染的示例代码
Nov 01 #Javascript
socket io与vue-cli的结合使用的示例代码
Nov 01 #Javascript
You might like
php xml文件操作代码(一)
2009/03/20 PHP
深入理解curl类,可用于模拟get,post和curl下载
2013/06/08 PHP
关于PHP自动判断字符集并转码的详解
2013/06/26 PHP
thinkphp学习笔记之多表查询
2014/07/28 PHP
php源码分析之DZX1.5加密解密函数authcode用法
2015/06/17 PHP
PHP访问数据库集群的方法小结
2016/03/14 PHP
thinkphp3.x中display方法及show方法的用法实例
2016/05/19 PHP
PHP date_default_timezone_set()设置时区操作实例分析
2020/05/16 PHP
javascript StringBuilder类实现
2008/12/22 Javascript
javascript实现仿IE顶部的可关闭警告条
2015/05/05 Javascript
JS中script标签defer和async属性的区别详解
2016/08/12 Javascript
网站申请不到支付宝接口、微信接口,免接口收款实现方式几种解决办法
2016/12/14 Javascript
工作中常用的js、jquery自定义扩展函数代码片段汇总
2016/12/22 Javascript
微信小程序 封装http请求实例详解
2017/01/16 Javascript
使用vue构建一个上传图片表单
2017/07/04 Javascript
基于AngularJS实现表单验证功能
2017/07/28 Javascript
使用typescript开发angular模块并发布npm包
2018/04/19 Javascript
AngularJS模态框模板ngDialog的使用详解
2018/05/11 Javascript
vue的toast弹窗组件实例详解
2018/05/14 Javascript
解决vue2.0 element-ui中el-upload的before-upload方法返回false时submit()不生效问题
2018/08/24 Javascript
vue 内置过滤器的使用总结(附加自定义过滤器)
2018/12/11 Javascript
详解ES6 export default 和 import语句中的解构赋值
2019/05/28 Javascript
Vue-cli打包后部署到子目录下的路径问题说明
2020/09/02 Javascript
python中的多线程实例教程
2014/08/27 Python
利用Python获取赶集网招聘信息前篇
2016/04/18 Python
PyTorch中Tensor的维度变换实现
2019/08/18 Python
Windows下pycharm创建Django 项目(虚拟环境)过程解析
2019/09/16 Python
Tensorflow设置显存自适应,显存比例的操作
2020/02/03 Python
html5图片上传预览示例分享
2014/04/14 HTML / CSS
捷克玩具商店:Bambule
2019/02/23 全球购物
Ajax实现页面无刷新留言效果
2021/03/24 Javascript
生物科学专业个人求职信范文
2013/12/05 职场文书
综合素质评价思想道德自我评价
2015/03/09 职场文书
通知书大全
2015/04/27 职场文书
赢在执行观后感
2015/06/16 职场文书
python的netCDF4批量处理NC格式文件的操作方法
2022/03/21 Python