1.闭包是什么?
- 闭包是一个具有作用域的代码块
{}
{var a=20}
if(){};
for(){};
label1:{}
//都不是闭包 虽然是封闭的 但没有作用域
有作用域的函数:
function fn(){
var a=20;
console.log(a) //打印局部变量a的值 20
}
fn();
console.log(a) //不能访问函数内部的局部变量 全局作用域中没有定义 所以报错
2.闭包的使用场景?
1)函数内部的变量外部是无法访问的 但可以通过返回一个闭包来访问
- 外部操作闭包来间接访问函数内部的变量,闭包可以决定这个变量的操作方式
- 利用了函数的作用域和运行时的作用域
案例1
function alipay() {
var money = 1000
//不能让函数之外的代码直接操作money 但是可以让自己的内部工具操作 然后返回这个工具
function tool() {
money -= 20
}
return tool
}
function meituan() {
var aplitool = alipay()
aplitool()
}
meituan()
2)利用函数每次调用时生成的独立调用栈
- 利用函数的形参保存临时变量的值
引例
var arr = []
for (var i = 0; i <5; i++) {
var obj = {}
arr.push(obj) //循环五次 生成五个空对象放入数组arr中
}
console.log(arr) //[{},{},{},{},{}]
for (var i = 0; i <= arr.length; i++) {
arr[i].age = i
arr[i].tool = function () {
console.log(i) //5
}
}
console.log(arr[2].age) //2
arr[3].tool() //for循环没有作用域 先执行完for循环再打印 即循环的最末值5
//每次放入的都是同一个i
案例
function tool (index) { //内部的形参变量是各自独立的
arr[index]=function fn(){
console.log(index)
}
}
var arr=[]
for(var i=0;i<5;i++){
tool(i) //函数每次调用传入的是不同的值
}
arr[2]()//2
优化
var arr=[]
for(var i=0;i<5;i++){
(function(index){
arr[index]=function fn(){
console.log(index)
}
})(i) //每次for循环让函数调用
} //函数调用过程中生成一个作用域内部有个形参变量index index就是保存的临时变量i的值
arr[2]() //2
3)利用函数的独立作用域 生成业务代码块 内部的变量相互不冲突不污染全局变量
- 用函数将代码块包起来 : (function(){})(); 一定要加分号
4)回调函数 利用函数复用的功能,制作复用工具,参数返回值
//执行业务 5000 行数据
//生成了一个数据 "hello"
function fn(arg1) {
var data = "hello"
arg1(data)
}
fn(function (a) {
console.log(a)
})
5)可以把一个闭包封装到原型中 这样每一个对象都可以共用闭包工具
3.闭包的优缺点
优点:
1.函数内部的变量想被外部使用 但是语法决定不能使用 可以利用闭包解决
2.一些临时数据 如循环的i的值 希望(延时)业务中使用 可使用闭包把临时数据保存到局部作用域中
3.防止变量污染 可以用闭包把业务变量放在局部作用域中
缺点:导致内存泄漏
内存泄漏:
- 浏览器运行网页的时候会执行js代码,引用数据会在内存中占用内存空间
- 如果有一个对象创建了且占用了内存但没有业务使用(用不了)就是内存泄漏
解决方案:
- 尽量避免使用闭包
- 在可能存在泄漏的地方把标识符引用为null
内存管理机制:垃圾回收机制 引用计数 ==>是底层浏览器的代码实现的功能
1.垃圾回收机制:系统定期查看代码的执行情况 观察创建的对象使用的可能性 若不再使用就释放内存
2.引用计数:每一个对象都有引用者,如果引用者为0就释放内存
function fn() {
var obj = {
age: 20
}
function tool() {
return obj.age
}
return tool //使用闭包
}
var re = fn() //fn函数调用完毕 内部的obj对象没有释放 会导致内存泄漏
console.log(re())
//解决方案:尽量不使用 或 在可能存在泄漏的地方把标识符引用为null
function fn() {
var obj = {
age: 20
}
function tool() {
return obj.age
}
return tool //使用闭包
}
var re = fn()
re = null;