前言
准确来说,闭包是基于正常的垃圾回收处理机制下的。也就是说,一般情况一个函数(函数作用域)执行完毕,里面声明的变量会全部释放,被垃圾回收器回收。但闭包利用一个技巧,让作用域里面的变量,在函数执行完之后依旧保存没有被垃圾回收处理掉。
闭包
定义
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
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]