闭包
概念 | 作用 |
---|---|
是一种函数,在函数中嵌套函数 | 读取函数内部的变量 |
内层的作用域访问它外层函数作用域 | 让这些变量始终保持在内存之中 |
变量作用域:全局作用域和局部作用域
- 函数内部可以使用全局变量
- 函数外部不可以使用局部变量
- 当函数执行完毕,本作用域的局部变量会销毁
function fn() {
var num = 10;
return function(){
console.log(num);//10
}
}
var f = fn();
f();//fn外部的作用域可以访问fn内部的局部变量
-
闭包应用场景
GC机制 : 在JavaScript中,如果一个对象不再被引用,那么这个对象就会被GC回收,否则这个对象一直会保存在内存中。
-
封闭作用域
封闭作用域 优点 封闭空间 1、不污染全局空间; 小闭包 2、内部所有的临时变量执行完毕都会释放不占内存; 匿名函数自调 3、可以保存全局数据; 全局变量私有化 4、更新复杂变量 (function (){ var age=18; console.log(age); })();//匿名函数不需要调用,进入到函数中就执行了,输出为18 (function(){ console.log(age); })();//报错了,age is not defined //虽然var age=18;是一个全局变量,但是在第二个函数中是拿不到的,因为它在一个封闭的作用域中。
-
作用域链
嵌套之间的函数会形成作用域链,每次对变量的访问实际上都是对整条作用域链的遍历查找,先查找最近的作用域,最后再查找全局作用域,如果在某个作用域找到了对量就会结束本次查找过程。
-
保存作用域
一句话:将数据绑定在指令上运行,让指令不再依赖全局数据。
var A=function(){ return function(){}; } <button id="btn">点我</button> (function(document){ var btn=document.getElementById("btn"); var btn1=document.getElementById("btn"); var btn2=document.getElementById("btn"); var btn3=document.getElementById("btn"); })(document)//通过第二个括号将全局变量传入到闭包中,使其变成局部变量,优化调用。也就是保存了全局的数据,局部直接使用。
-
高级排他
<style> li{ background-color: #ccc; border: 1px solid #000; cursor: pointer; } .current{ background-color: red; } </style> <ul> <li class="current"></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> 普通写法: var allLis = document.getElementsByTagName("li"); for (var i = 0; i < allLis.length; i++) { //第一次 遍历所有的li allLis[i].onmouseover = function () { for (var j = 0; j < allLis.length; j++) {//第二次 每触发一次鼠标事件都重新遍历所有的li allLis[j].className = "";//鼠标经过一次就清空所有行 } this.className = "current";//再给当前的行加上类名 } } 提高性能: var allLis = document.getElementsByTagName("li"); var lastOne = 0; for (var i = 0; i < allLis.length; i++) { //遍历所有的li (function (index) { allLis[i].onmouseover = function () { //每鼠标经过一次先清空上一次的 allLis[lastOne].className = ""; //再给当前的li添加 this.className = "current"; //记录当前的索引 lastOne = index; }; })(i);//利用闭包,传入每次循环的当前索引i,从而“锁定”鼠标经过事件时传入对应的索引值i }
-
参数传递
//传统方法,使得图片移动 var left = 0, right = 0, speed = 50; var lImg = document.getElementById("l"); var rImg = document.getElementById("r"); lImg.onmousedown = function () { left -= speed; this.style.marginLeft = left + "px"; }; rImg.onmousedown = function () { right += speed; this.style.marginLeft = right + "px"; }; //闭包(当两个事件同步的时候) function move(speed) { var num = 0; return function () { num += speed; this.style.marginLeft = num + "px"; }; } var lImg = document.getElementById("l"); var rImg = document.getElementById("r"); lImg.onmousedown = move(-50); rImg.onmousedown = move(50); //输入的参数是独立的,不会影响。
-
函数防抖与节流
防抖:我们输入每一个字符时,都会发送一次请求,增大了服务器的压力
-
节流:当稍微改变浏览器窗口大小,就会执行很多次业务,这样**浏览器性能将会受影响,可能会造成卡顿**
```js
window.onresize = function(){
console.log("执行了一些业务")
}
```
为了解决这样的问题,就需要实现防抖与节流了
##### 实现防抖:
```js
function debounce(callback,delay){
//定义一个计时器
timer = null
return function(...args){
//如果进来的时候有计时器
if(timer){
//清除计时器
clearTimeout(timer)
}
timer = setTimeout(()=>{
// callback(args[0])
callback.apply(this,args)
},delay)
}
}
```
闭包的作用,我们把time放在了函数中,**避免了全局污染**
##### 实现节流:
```js
window.onresize = throttle(fn,1000)
function fn(){
console.log("执行了一些业务")
}
function throttle(callback,delay){
//判断是否正在定时
let flag = false
return function(...args){
if(flag == true){
return
}
//如果不在定时,则开始定时
flag = true
setTimeout(()=>{
callback.apply(this,args)
//执行完成,结束定时
flag = false
},delay)
}
}
```
-
延长局部变量的生命 (解决数据丢失的问题)
var report = function (src) { var img = new Image();//当report函数调用结束后,Image对象随即被JS引擎垃圾回收器回收 img.src = src; }; report("http://www.xxx.com/getClientInfo"); //可能还没来得及发出http请求,所以可能导致此次上报数据的请求失败 使用闭包把Image对象封闭起来,就可以解决数据丢失的问题: var report = (function() { var imgs = [];//在内存里持久化 return function(src) { var img = new Image(); imgs.push(img);//引用局部变量imgs img.src = src; } }()); report('http://www.xxx.com/getClientInfo');//把客户端信息上报数据
8. ##### 占用内存
函数执行完成后,函数内部的局部变量没有释放,占用内存时间会边长,容易造成内存泄露。
```js
function fn() {
var num = 10;
return function(){
console.log(num);//10
}
}
var f = fn();
f();
f = null;//释放内存
```
9. ##### 垃圾回收机制(GC)
JS中的内存的分配和回收都是自动完成的,内存在不适用的时候会被垃圾回收器自动回收。
- 内存的生命周期
1. 内存分配:声明变量、函数、对象时,系统自动分配内存
2. 内存使用:即读写内存,也就是使用变量、函数
3. 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
- 说明:
全局变量一般不会回收(关闭页面回收)
一般情况下局部变量的值,不用了,会被自动回收
- 内存泄漏
程序中分配的内存由于某种原因程序未释放或无法释放
-
基本数据类型:string,number、boolean、undefined、null
复杂数据类型(引用数据类型):Object、Array、Date等 -
堆栈空间分配区别:
-
栈:函数参数值、局部变量的值等、基本数据类型放到栈里面
-
堆:复杂数据类型放到堆里面
-
-
简单数据类型 是存放在栈里面 里面直接开辟一个空间存放的是值
-
复杂数据类型 首先在栈里面存放地址 十六进制表示 然后这个地址指向堆里面的数据
-
引用计数法:
- IE浏览器使用:看一个对象是否有指向它的引用,没有引用了就回收对象
- 引用一次,+1,多次引用会累加++
- 减少一个引用 -1 –
- 引用次数是0,则释放内存
- 循环引用:两个对象相互引用,引用次数永远不会是0,导致内存泄露
- IE浏览器使用:看一个对象是否有指向它的引用,没有引用了就回收对象
-
标记清除法:
- 将 “不再使用的对象” 定义为 “无法到达的对象”
1. 从根部(全局对象)扫描对象,能查到的就是使用的,查不到的就要回收
- 将 “不再使用的对象” 定义为 “无法到达的对象”
-
-