闭包
什么是闭包
一个函数使用了它外面的变量,这种用法就是闭包。闭包是一个马后炮的总结。
function xxx(){
var lives = 30
var bug = 'salkdjaslkdjaslkjd...100MB' // IE bug
function die(){
lives -= 1
return lives
}
return die
}
var dieFn = xxx()
// here
var currentLives = dieFn()
那为何要这样做呢(搞得这么麻烦):
闭包的作用
闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」。
因为如果是全局变量,容易被改,如果是局部变量,别人又访问不到。
上面这样用闭包,就可以用dieFn()来修改lives。
闭包造成内存泄露?
内存泄露是指你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。
闭包里面的变量明明就是我们需要的变量(lives),所以不是内存泄露
为何有人说是?
因为 IE。IE 有 bug,IE 在我们使用完闭包之后,依然回收不了闭包里面引用的变量。
立即执行函数
什么是立即执行函数
声明一个匿名函数,立即执行它,就是立即执行函数
!function (){
var lives = 30
console.log(lives)
}.call()
感叹号可以换成 + - ~ 等符号,也可以换成括号。
那为什么要有这么个东西(好麻烦)
立即执行函数的作用
只有一个作用:创建一个独立的作用域。
这个作用域里面的变量,外面访问不到(即避免「变量污染」)。
这个作用不就恰恰是闭包所需要的吗!!!
所以之前的函数可以写成
!function xxx(){
var lives = 30
var bug = 'salkdjaslkdjaslkjd...100MB' // IE bug
function die(){
lives -= 1
return lives
}
return die
}.call()
举例:
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
liList[i].onclick = function(){
alert(i) // 为什么 alert 出来的总是 6,而不是 0、1、2、3、4、5
}
}
因为在点击之前i早变成了6,每个监听的元素都为6。
那么怎么解决这个问题呢?用立即执行函数给每个 li 创造一个独立作用域即可
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
!function(ii){
liList[ii].onclick = function(){
alert(ii) // 0、1、2、3、4、5
}
}(i)
}
在立即执行函数执行的时候,i 的值被赋值给 ii,此后 ii 的值一直不变。
i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的 ii 「分别」是 0、1、2、3、4、5。
异步+回调
什么是异步
同步:一定要等任务执行完了,得到结果,才执行下一个任务。
function taskSync = function(){
return '同步任务的返回值'
}
var result = taskSync() // 那么 result 就是同步任务的结果
otherTask() // 然后执行下一个任务
异步:不等任务执行完,直接执行下一个任务。相当于给前一个任务加个警报器,任务好了再告诉你去执行。
function taskAsync = function(){
var result = setTimeout(function(){
console.log('异步任务的结果')
}, 3000)
return result
}
var result = taskAsync() // result 不是异步任务的结果,而是一个 timer id。不懂?因为现在我是无法得到3秒后的result,只会得到他的定时器。
otherTask() // 立即执行其他任务,不等异步任务结束
什么情况下需要用到异步?
如果几个任务互相独立,其中一个执行时间较长,那么一般就用异步地方式做这件事。
什么是回调
callback 就是(传给另一个函数调用的)函数。把括号里面的内容去掉,简化成:callback 就是一种函数。
具体来讲:
当一个函数 A 被作为参数传给另一个函数时 B,那么这个函数 A 就叫做回调(名词)。B 中调用 A 函数的过程,也叫做回调(动词)。
那回调有什么用呢?
回调的作用
回调通常用在获取「异步任务」的结果
之前异步的代码也可写成如下(为理解起见我简化了)
function async(fn){
setTimeout(function(){
fn('异步任务的结果')
}, 3000)
return
}//函数声明
async(function (xxx){
console.log(xxx)
}) // 函数调用。3秒后执行fn,xxx 是异步任务的结果
otherTask()
过程简单来说就是我调了async函数,然后在async函数里它调了fn函数(此时fn相当于是我传的参数function),调用的时候把'异步任务的结果'(此时'异步任务的结果'相当于xxx)传了出来。
其中function (xxx){ console.log(xxx)}和fn('异步任务的结果')都是回调,一个是名词,一个是动词。