一、闭包是什么?
闭包是可以访问独立变量的函数。因为函数是可以创造作用域的,函数的内部作用域是可以访问外部作用域,但是外部作用域是不能访问内部作用域的,所以在函数内声明的变量可以通过声明的内函数进行访问,然后把这个内函数返回,就可以在函数外面直接访问函数内的独立变量。
作用域链:就是函数可以创造作用域,在函数内部也可以声明函数,这就形成了作用域套作用域的链式结构,简称为作用域链。js分为内部作用域和外部作用域,访问变量都是从内向外沿着作用域链进行查找的。
二、闭包需要解决的问题是什么?
在函数外部访问函数内部声明的变量。
三、闭包的基本模式:
//1.基本模式:
function outer(){
var data=10;
function inner(){
return data;
}
return inner;
}
var fn=outer();
fn();
//2.获取独立变量和设置独立变量:
function outer(){
var data=10;
function getData(){
return data;
}
function setData(value){
data=value;
return data;
}
return {
getData:getData,
setData:setData
};
}
var fn=outer().setData;
fn(100); //100
fn=outer().getData;
fn(); //10
//3.优化代码
function outer(){
var data=10;
function inner(value){
if(value){
data=value;
}
return data;
}
return inner;
}
var fn=outer();
fn();
总结起来就是:返回一个函数,用这个函数获得数据;返回一个对象,这个对象包含函数,来操作这个数据
四、循环点击事件存在的问题及解决方案:
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div{
border:solid 1px #000;
margin-bottom: 10px;
width:70px;
height:20px;
}
</style>
<script>
window.onload=function(){
var divs=document.getElementsByTagName("div");
for(var i=0;i<divs.length;i++){
var div=divs[i];
div.onclick=function(){
console.log("我是第"+i+"个div");
}
}
}
</script>
</head>
<body>
<div>第0个div</div>
<div>第1个div</div>
<div>第2个div</div>
<div>第3个div</div>
<div>第4个div</div>
<div>第5个div</div>
<div>第6个div</div>
</body>
</html>
期待的效果是:点击“第0个div”,显示“我是第0个div”,点击“第1个div”,显示“我是第1个div”。但是实际显示的是点击任何一个,显示的都是,“我是第7个div”。
//原因:因为在触发div点击onclick事件的时候,for循环已经执行完毕,这时候返回的i就是7,所以点击每一个div都是返回7
解决办法:使用闭包:将i变量保护起来
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div{
border:solid 1px #000;
margin-bottom: 10px;
width:70px;
height:20px;
}
</style>
<script>
window.onload=function(){
var divs=document.getElementsByTagName("div");
for(var i=0;i<divs.length;i++){
var div=divs[i];
function outer(){
var j=i;
return function(){
console.log("我是第"+j+"个div");
}
}
div.onclick=outer(); //利用闭包解决了循环点击出现的问题
}
}
</script>
</head>
<body>
<div>第0个div</div>
<div>第1个div</div>
<div>第2个div</div>
<div>第3个div</div>
<div>第4个div</div>
<div>第5个div</div>
<div>第6个div</div>
</body>
</html>
五、for循环中的setTimout出现的问题:
<script>
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i);
},0);
}
var a=10;
alert(a);
</script>
setTimout(function(){},time)是至少在time时间后执行回调函数。在js中有分主要任务和次要任务,主要任务就是主逻辑;次要任务就是setTimout和setInterval等回调函数。所以上述代码会先for循环,和setTimeout,执行下面的alert 10,再执行回调函数console.log(i),所以最终返回的也是10次10.
解决办法:利用闭包保护i变量:
<script>
for(var i=0;i<10;i++){
function outer(){
var j=i;
function inner(){
console.log(j);
}
return inner;
}
setTimeout(outer(),0);
}
</script>
六、斐波拉契数列的性能问题:
<script>
//1. 用递归的方法实现斐波拉契数列:
//增加count计数来计算每次fib函数执行的次数:
//存在的问题:重复计算的次数太多,
var count=0;
function fib(n){
count++;
if(n<=2){
return 1;
}else{
return fib(n-1)+fib(n-2);
}
}
fib(5);
console.log(count); //9
count=0;
fib(20);
console.log(count); //13529
//2. 优化时间复杂度,可以使用非递归的方式实现斐波拉契数列:
var count1=0;
function fib1(n){
var a, b,res;
a=b=1;
if(n<=2){
return 1;
}else{
for(var i=3;i<=n;i++){
count1++;
res=a+b;
a=b;
b=res;
}
return res;
}
}
fib1(20); //6765
console.log(count1); //18
</script>
用递归实现的斐波拉契数列,就存在大量的重复计算,这种重复计算会导致count是成倍成指数型的增加,耗费计算机内存。
除了使用我上面代码中提到的用非递归的方式进行实现,还有一种方法也可以:就是将每次计算出来的fib(n)值存到数组里,这样如果有就直接取,没有就计算,计算好了就存;这样也是大大减少了重复计算量。
//3.利用数组存储的方式优化递归实现的斐波拉契数列算法:
var arr=[];
var count2=0;
function fib2(n){
count2++;
var num=arr[n];
if(num){
return num;
}else{
if(n<=2){
return 1;
}else{
num=fib2(n-1)+fib2(n-2);
arr[n]=num;
return num;
}
}
}
fib2(20); //5
console.log(count2);
除此之外,这个arr变量暴露在全局,很可能被别人随意修改,我们可以通过闭包的方式保护这个变量:
//4.利用闭包优化递归实现的斐波拉契数列算法:(保护了数组变量arr)
function fib3(){
var arr=[];
function fibIn(n){
var num=arr[n];
if(num){
return num;
}else{
if(n<=2){
return 1;
}else{
num=fib2(n-1)+fib2(n-2);
arr[n]=num;
return num;
}
}
}
return fibIn;
}
var fn=fib3();
fn(5);
七、Github源码地址:
https://github.com/spicyboiledfish/JavaScript-testJS