闭包是一个函数,可以访问独立数据的函数,
在一个作用域中可以访问另一个作用域的变量或者函数(嵌套的函数 访问了它外部的变量!!! (也可以是函数)
访问的外部函数是匿名函数时,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 也是放在任务队列上