闭包难,但是闭包也是JavaScript语言的一个特色,很多高级应用都需要闭包实现
变量的作用域
- js 变量的作用域分为全局作用域和局部作用域
- js语言的特殊之处就是在于函数内部可以直接访问全局声明的变量
- 但是在函数外部是无法访问函数内部的变量的
- 在函数内部声明一个变量的时候,一定要加上var,否则相当于声明一个全局变量
如何从外部获取函数内部的局部变量
- 正常情况下,如果我们想要在函数外部获取一个函数内部的局部变量这是办不到的,这时候我们就需要一个变通的方法来实现,我们可以在想要获取那个局部变量的函数里面加上一个方法(函数),我们把这个函数return出去即可。这里涉及到一个js链式作用域结构
- 什么是链式作用域?
- js中子对象是可以一级一级的向上寻找父对象的所有变量的,所以,父对象的所有变量对子对象都是可见的,但是反之不行。
闭包的概念
闭包的本质就是一座桥梁,这座桥梁把函数的内外两部分连接了起来,使得外部可以访问内部
闭包的作用
让函数外部可以拿到函数内部的局部变量
让某些变量的值始终保存在内存中,而不被js的垃圾回收机制回收
使用闭包的注意点
闭包会使得函数内部的变量都会被保存在内存中,不会被释放,内存消耗很大,所以闭包不能滥用;否则会造成网页的性能问题,在IE中还可能会造成内存泄漏。
解决办法是:在退出函数之前,将不使用的局部变量手动销毁(删除)
闭包会在函数外部访问函数内部的局部变量,甚至是可以改变这个局部变量,所以使用时一定要慎重
研究内存泄漏之前先要搞明白为什么程序运行需要内存?
- 什么是内存?
从硬件和逻辑两个角度来看,硬件上内存是一个内存条,只是计算机的一个配件
逻辑上内存可以随机访问,只要提供一个地址,就可以根据这个地址寻找内存上的存储位,内存可以读或写,最后内存可以存储变量,内存中的一个单元就可以存储一个变量
- 计算机程序的运行目的是什么?
计算机的运行目的是为了计算,计算就是在计算数据,所以程序=代码+数据;计算机程序运行完以后会得到一个结果,也就是代码+数据(运行)= 结果。
从宏观上来理解,代码是动作,加工程序的动作,数据是数字,就是需要被加工的东西,所以程序的运行只有两个目的—结果和过程。可以用一个函数来类比,形参,代码(函数体),返回结果,过程就是执行过程。
- 计算机程序的运行过程?
程序是由很多函数构成的,程序的本质就是函数,函数的本质就是加工数据的动作。
- 为什么需要内存?
内存是用来存储可变数据的,数据在程序中表现为全局变量和局部变量,可以说内存对于程序来说是本质需求。
JavaScript内存泄漏
- 什么是内存泄漏?
不在用到的内存,没有及时释放,就叫做内存泄漏(memory leak)
对于C语言来说,必须手动释放不需要使用的变量,程序本身不会自动释放,但是对于大多数语言来说程序本身都会自动管理内存,这被称为‘垃圾回收机制’。
- 垃圾回收机制怎么知道,哪些内存不再需要呢?
最常使用的方法叫做"引用计数",还可以使用‘标记清除’。
- 怎样可以观察到内存泄漏呢?
经验法则是,如果连续五次垃圾回收之后,内存占用一次比一次大,就有内存泄漏。这就要求实时查看内存占用
再次简识
闭包原理
- 闭包实际上是一个函数
- 一般两个函数就可以构成闭包,内部函数引用外部函数的变量,内部函数作为外部函数的返回值
function outer() {
var num = 9
function inner() {
console.log(num * 9)
}
return inner
}
var fn = outer()
fn()
闭包的使用形式
- 将函数作为另外一个函数的返回值
- 将一个函数的形参作为一个函数的实参传递,比如节流函数
window.onresize = throttle(function() {
// 待处理的业务逻辑
}, 1000)
function throttle(fn, delay) {
var timer = null
return function() {
clearTimeout(timer)
timer = setTimeout(fn, delay)
}
}
应用场景
- 点击按钮,提示当前点击的是哪个
window.onload = function() {
var btns = document.getElementsByTagName('button')
for (var i = 0; i < btns.length; i++) {
(function(i) {
btns[i].onclick = function() {
alert(i+1)
}
})(i)
}
}
- 鼠标滑动,给当前的元素显示高亮
window.onload = function() {
var lis = document.getElementsByTagName('li')
// 当前被激活的元素的索引为0
var preIndex = 0
for (var i = 0; i < lis.length; i++) {
(function(i) {
lis[i].onmouseover = function() {
// 清除先前
lis[preIndex].className = ''
// 设置当前
this.className = 'active'
// 更新索引
preIndex = i
}
})(i)
}
}
- 模块封装
;(function(w) {
var num = 8
function set(x) {
num = num*x
}
function get() {
return num
}
w.tools = {
set: set,
get: get
}
})(window)
内存泄漏
- 程序报错会导致泄漏
- 占用内存没有被及时释放也会导致
- 内存泄漏可能会导致内存溢出
- demo:占用内存很大的局部变量,定时器/计时器,闭包
闭包优点
- 减少原型链查找,提升性能
- 延长局部变量的生命周期
闭包缺点
- 函数执行完后,函数内部的局部变量不能被及时释放,占用内存的时间变长,容易造成内存溢出