从「闭包」到 思考人生
闭包的定义
A closure is the combination of a function and the lexical environment within which that function was declared. --
闭包是函数和声明该函数的词法作用域的集合。
A closure is the local variables for a function - kept alive after the function has returned . --
闭包是一个函数的局部变量 ——— 在函数返回后仍然有效
不过关于闭包的定义,我更喜欢知乎上的一个答案,简洁明了,便于理解。
一个持有外部环境变量的函数就是闭包。-- 来自知乎Saviio
参考:
闭包的作用
闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」。
词法作用域 (lexical environment)
之前写过一个关于 的博客
- 函数在执行的过程中,先从自己内部找变量
- 如果找不到,再从创建当前函数所在的作用域(词法作用域)去找, 以此往上
- 注意找的是变量的当前的状态
函数连同它作用域链上的要找的这个变量,共同构成闭包
一般情况下使用闭包主要是为了
- 封装数据
- 暂存数据
一个典型的闭包案例
function car(){ var speed = 0 function fn(){ speed++ console.log(speed) } return fn } var speedUp = car() speedUp() //1 speedUp() //2
闭包相关案例
当然,你可以不看这些操蛋该死的案例,直接拖到最后,我们聊聊人生哲理
案例1
如下代码输出是怎样的?
如果想输出3,应该如何修改代码?
var fnArr = []; for (var i = 0; i < 10; i ++) { fnArr[i] = function(){ return i }; } console.log( fnArr[3]() ) // 10
//改造 1 var fnArr = [] for (var i = 0; i < 10 ;i++){ fnArr[i] = (function(j){ return function(){ return j } })(i) } console.log(fnArr[3]()) //改造 2 使用立即函数声明表达式 var fnArr = [] for (var i = 0; i < 10 ;i++){ (function(i){ fnArr[i] = function(){ return i } })(i) } console.log(fnArr[3]()) //改造 3 使用ES6 let代替var var fnArr = [] for (let i = 0; i < 10 ;i++ ){ fnArr[i] = function(){ return i } } console.log(fnArr[3]())
案例2
封装一个 Car 对象
var Car = (function(){ var speed = 0 function set(s){ speed = s } function get(){ return speed } function speedUp(){ speed++ } function speedDown(){ speed-- } function speedAdd(a){ speed = speed+a } function speedSub(a){ speed = speed-a } return { set: set, get: get, speedUp: speedUp, speedDown: speedDown, speedAdd: speedAdd, speedSub: speedSub } })() //调用 Car.set(30) Car.get() //30 Car.speedUp() Car.get() //31 Car.speedDown() Car.get() //30 Car.speedAdd(10) Car.get() //40 Car.speedSub(20) Car.get() //20
案例3
如下代码输出是怎样的?
怎样修改代码让其连续输出 0,1,2,3,4
for(var i = 0; i < 5 ;i++){ setTimeout(function(){ console.log('delayer: ' + i ) },0) } //输出5个5
//方式1 for(var i = 0; i < 5 ;i++){ setTimeout((function(j){ return function(){ console.log('delayer: ' + j ) } }(i)),0) } //方式2 使用立即函数声明表达式 for(var i = 0; i < 5 ;i++){ (function(j){ setTimeout(function(){ console.log('delayer: ' + j ) },0) })(i) } //方式3 使用ES6 的let for(let i = 0; i < 5 ;i++){ setTimeout(function(){ console.log('delayer: ' + i ) },0) }
该案例即是一个倒数计时器的模型
//倒数计时器 for(var i = 0; i < 5 ;i++){ (function(j){ setTimeout(function(){ console.log('delayer: ' + j ) },5000 - 1000*j)
案例4
独立作用域 与 活动对象
//如下代码输出为多少 function makeCounter(){ var count = 0 return function(){ return count++ } } var counter = makeCounter() //生成作用域 有一个活动对象count var counter2 = makeCounter() //生成作用域 也有一个活动对象count 和之前的是两个不同的作用域 相互独立 console.log(counter()) //0 console.log(counter()) //1 console.log(counter2()) console.log(counter2()) //所以counter2输出也是0 1 和之前的counter无关
案例5
实现数组按姓名、年纪、任意字段排序
之前在 博客中谈及sort
方法的时候也提出过这个案例,即对数组进行排序
这里我们将这个方法进行升级,利用闭包的特性,使我们可以自行输入类型,通过users.sort(byField('field'))
来进行排序
var users = [ { name: 'John', age : 20, company: 'Baidu' }, { name: 'Pete', age : 18, company: 'Alibaba' }, { name: 'Ann', age : 19, company: 'Tecent' } ] function byName(user1 ,user2){ return user1['name'] > user2['name'] // return user1.name > user2.name } function byAge(user1 ,user2){ return user1['age'] > user2['age'] // return user1.age > user2.age } function byField(field){ return function(user1 ,user2){ return user1[field] > user2[field] } } users.sort(byName) //按照名字排序 users.sort(byAge) //按照年龄排序 users.sort(byField('company')) //定义按照任意输入的类型进行排序的方法
案例6
定义一个sum函数,实现如下调用
console.log( sum(1)(2) ) // 3 console.log( sum(5)(-1) ) // 4
function sum(a) { return function(b) { return a + b } }
闭包案例 Github
写在最后 关于最震撼的答案
或许我们不需要真的懂闭包?
假设下面三行代码在一个立即执行函数中
注意:下面三行代码在一个立即执行函数中
注意:下面三行代码在一个立即执行函数中
注意:下面三行代码在一个立即执行函数中
三行代码中,有一个局部变量 local,有一个函数 foo,foo 里面可以访问到 local 变量。
这就是一个闭包:
第一句是变量声明,第二句是函数声明,第三句是 console.log。
每一句我都学过,为什么合起来我就看不出来是闭包?
答案是,你根本不需要知道闭包这个概念,一样可以使用闭包!
闭包是 JS 函数作用域的副产品。
换句话说,正是由于 JS 的函数内部可以使用函数外部的变量,所以这段代码正好符合了闭包的定义。而不是 JS 故意要使用闭包。
很多编程语言也支持闭包,另外有一些语言则不支持闭包。
只要你懂了 JS 的作用域,你自然而然就懂了闭包,即使你不知道那就是闭包!
关于
那些所谓的闭包的作用
如果我们在写代码时,根本就不知道闭包,只是按照自己的意图写,最后,发现满足了闭包的定义。
那么请问,这算是闭包的作用吗?
真的写在最后
摘自 -- JS 中的闭包是什么?
编程界崇尚以简洁优雅为美,很多时候
如果你觉得一个概念很复杂,那么很可能是你理解错了。