理解Javascript闭包

一、什么是闭包

GC 垃圾回收

复制代码
通常情况下,函数被调用后,内部的变量 会自动回收,不用的变量 内存自动释放

特殊情况下,例函数里的变量 还会被 用到的就不会被回收   例: 3按钮 点击按钮 弹出 索引+1

1. 闭包的底层:栈

复制代码
计算机 每调用一次函数时,内存就会 在内存中开辟一块空间,这个空间叫栈
整个函数的所有局部变量都在这个栈中运行

栈的本质:函数执行时会分配空间,函数结束时会回收

闭包可以做到的是:函数执行完了,栈也不回收,这就叫闭包

2.把函数当作对象处理

复制代码
对于某些语言来说,例java,不能存在独立的方法,一定是寄存在某个对象上,不管是静态的动态的,而js不一样,js中有独立存在的函数,只要函数调用时,它就会创建一个对象出来,这个对象就包含函数的各种细节
js 复制代码
function show(){
    let a=12;
    document.onclick=function(){
            alert(a);
    }
}
// 总结:尽管 a 是局部变量,但它不会被回收,因为 document.onclick 还会用到它

二、闭包的优缺点

js 复制代码
闭包的好处:
  延展了函数的局部作用域 的生命周期
  正常情况下,一个函数被调用后,它的作用域 就会被销毁;
  但当有闭包的情况时,局部作用域暂时不会销毁

闭包的缺点:
    不能立即释放作用域,销毁局部作用域

三、产生闭包的几种情况

php 复制代码
闭包是一个函数,可以访问独立数据的函数,
在一个作用域中可以访问另一个作用域的变量或者函数(嵌套的函数 访问了它外部的变量!!! (也可以是函数)
访问的外部函数是匿名函数时,Closure: 没有名字的函数)

function fn() {   var n = 10;     return n; }
fn();  // 未发生闭包:

发生闭包:   函数作为返回值

1.实例:获取1-10的随机数

js 复制代码
// 获取1-10的随机数
function getRandom(){
    var random = parseInt(Math.random()*10) + 1;
    return function(){
            return random;
    }
}
var fn = getRandom();
console.log(fn());

2.实例:计算两个数的和

js 复制代码
// 计算两个数的和
function getSum(n){
    return function(m){
            return n + m;
    }
}
// 求 100 + m
var fn = getSum(100);
// 求 1000 + m
var fn1000 = getSum(1000);

console.log(fn(1)); // 101
console.log(fn1000(1)); // 1001

3.实例:点击ul下的li,打印li对应的索引值

js 复制代码
// 方式一:
var heros = document.getElementById('heros');
var list = heros.children;   // 获取ul下的所有li
for (var i = 0; i < list.length; i++) {
	var li = list[i];
	li.index = i;
	li.onclick = function () {    // 给 li 注册点击事件
		console.log(this.index);  // 输出当前li对应的索引
	}
}

// 方式二:闭包
var heros = document.getElementById('heros');
var list = heros.children;
for (var i = 0; i < list.length; i++) {
    var li = list[i];

    (function(i){
            li.onclick = function () {
            console.log(i);
            }
    })(i)
}
// 不是学会闭包就一定要使用闭包,因为闭包也会带来一些问题,例上述的案例,每次循环都要创建一个自调用function,并且这个函数内部会创建作用域,因为这个函数发生了闭包,所以这个作用域并没有立即被释放,降低了性能(因为使用闭包,它延展了作用域,没有立即释放作用域)

4.实例:当for循环遇到定时器

js 复制代码
console.log('start');
for(var i = 0; i < 3; i++) {
	setTimeout(function(){
	  console.log(i);
	},0)
}
console.log('over'); // 控制台输出结果:start over 3个3  因为for循环的时机和setTimeout的执行时机不一样,当执行setTimeout时,循环早已结束

// 解决 function 中 输出的值 为 0 1 2
console.log('start');
for(var i = 0; i < 3; i++) {
  (function(i){
    setTimeout(function(){
      console.log(i);
    },0)
  })(i)
}
console.log('over'); 

5.实例:点击相应按钮i,字体变化

js 复制代码
// 普通方式:
var btn01 = document.getElementById('btn01');
var btn03 = document.getElementById('btn02');
var btn03 = document.getElementById('btn03');
btn01.onclick = function(){ document.body.style.fontSize = '12px';}
btn02.onclick = function(){ document.body.style.fontSize = '14px';}
btn03.onclick = function(){ document.body.style.fontSize = '16px';}


// 闭包方式:
var btn01 = document.getElementById('btn01');
var btn03 = document.getElementById('btn02');
var btn03 = document.getElementById('btn03');

// 创建一个函数,设置body字体的大小
function makeFun(size){
  return function(){
    document.body.style.fontSize = size +'px';
} }

btn01.onclick = makeFun(12);
btn02.onclick = makeFun(14);
btn03.onclick = makeFun(16);

// 循环+闭包的方式:
<div id="box">
  <button size="12">按钮1</button>
  <button size="14">按钮2</button>
  <button size="16">按钮3</button>
</div>

// js:创建一个函数,设置body的字体大小
function makeFun(size) {
  return function () {
    document.body.style.fontSize = size + 'px';
  }
}
var box = document.getElementById('box');
var buttons = box.children;
for (var i = 0; i < buttons.length; i++) {
  var btn = buttons[i];
  // 获取标签的自定义属性
  var size = btn.getAttribute('size');
  btn.onclick = makeFun(size);
}

四、定时器是如何工作的?

javascript 复制代码
console.log('start');
setTimeout(function(){
	console.log('timeout');
},0)
console.log('over'); // 输出结果:start  over  timeout

// 函数的执行 概念:
// 1.执行栈:先执行 执行栈上的函数,再去执行 任务队列中的函数
// 2.任务队列:setTimeout( ) 是一个函数,其中 function 作为参数 会被放在任务队列
//   此时函数并没有执行,而是在队列中排队
//   注册事件的 处理函数 function 也是放在任务队列上
相关推荐
拉不动的猪3 分钟前
前端常见数组分析
前端·javascript·面试
小吕学编程20 分钟前
ES练习册
java·前端·elasticsearch
Asthenia041227 分钟前
Netty编解码器详解与实战
前端
袁煦丞32 分钟前
每天省2小时!这个网盘神器让我告别云存储混乱(附内网穿透神操作)
前端·程序员·远程工作
一个专注写代码的程序媛2 小时前
vue组件间通信
前端·javascript·vue.js
一笑code2 小时前
美团社招一面
前端·javascript·vue.js
懒懒是个程序员2 小时前
layui时间范围
前端·javascript·layui
NoneCoder2 小时前
HTML响应式网页设计与跨平台适配
前端·html
凯哥19702 小时前
在 Uni-app 做的后台中使用 Howler.js 实现强大的音频播放功能
前端
烛阴2 小时前
面试必考!一招教你区分JavaScript静态函数和普通函数,快收藏!
前端·javascript