小概
本章的学习内容:闭包,递归,自执行函数
1. 闭包
1.1 什么是闭包
1.声明在函数内部,能够访问函数内部的局部变量的这么一个函数
2.闭包是函数内部和函数外部的一个连接桥梁
3.闭包是一个不会被销毁的封闭的环境
function test1(){
let num = 10; //局部变量的生命周期,函数执行完就回收
function test2(){
console.log(num);
}
return test2;
}
let fn = test1(); //调用test1函数有一个返回值,返回值就是test2函数本身, 用全局变量fn接收.
//全局变量生命周期是直到程序关闭的
fn();
在函数test1中声明的num变量,函数外部是无法访问的,通过闭包(test2拿num,test1返回test2)使外部拿到函数内部声明的变量,而且因为test2调用了num,函数test1执行完后并不会回收变量num,提升了变量的生命周期。
1.2 闭包的作用
1.提升变量的生命周期
2.提供有限的访问权限
3.声明私有属性
//闭包的作用02: 提供有限的访问权限.
function outer(){
let age = 18;//年龄
//取值
function getAge(){
return age;
}
//赋值
function setAge(value){
//写判断逻辑代码.
if(value >0 && value <= 140){
age = value;
}
}
//返回
return {
getAge:getAge,
setAge:setAge
}
}
let obj = outer(); //调用outer会得到一个返回值,返回值就是一个对象,这个对象里面有2个方法,一个getAge,一个setAge
obj.setAge(28);
console.log(obj.getAge());
在闭包setAge函数中添加判断条件,没满足条件就不能对age继续操作,提供有限的访问权限
//2.写一个带私有属性的构造函数.
function Student(name,age1){ //形参本质就是函数内部的局部变量.
let age = age1;
this.name = name;
//设置age的值
this.setAge = function(value){
if(value > 0 && value <= 140){
age = value;
}
}
//获取age的值
this.getAge = function(){
return age;
}
}
let s1 = new Student('德华',22);
//到这一步我只能通过方法去访问age了
s1.setAge(40);
console.log(s1.getAge());
1.3 使用闭包的注意点
如果希望每次使用的局部变量是同一个,那外部的函数只能调用一次.
如果希望每次使用的局部变量不是统一而,那每次都要调用外部函数.
function outer(){
let num = Math.floor(Math.random()*100); //局部变量是0-99之间的一个数.
function inner(){
console.log(num);
}
return inner;
}
//1.1 三次调用num的值都一样,因为outer只执行一次,那num只声明一次,后面三次都是对他做一个输出而已.
let fn = outer();
fn();
fn();
fn();
//1.2 三次调用num的值都不一样,因为outer每次都执行了,每次都执行都会重新声明一个num,每次输出都是输出那个新的num.
outer()();
outer()();
outer()();
闭包虽然可以提升变量生命周期,但是也可能造成内存泄露,所以退出闭包的时候将不要的变量删除,或者直接赋值为null;闭包应用场景:模块化,封装代码。
1.4 沙箱/沙盒
//沙箱/沙盒
//其实就是一个密闭的环境,不能主动影响外界
//在js中其实就是一个自执行函数.
//沙箱其实就是闭包的一个应用.
//1.沙箱有什么用?
//1.1 避免全局变量污染.
//1.2 模块化开发
(function(w){
let num = 10;
function test1(){
console.log(num);
}
//把需要暴露给外界的东西暴露出去.
//1.避免破坏函数的封装性.
//2.代码上线的时候会压缩,避免压缩代码出现问题.
w.test1 = test1;
// w.test2 = test2;
}(window));
test1();
把闭包需要暴露出去的挂载到window上
2. 递归!
定义:函数内部自己调用自己
递归一定要有结束的时候,不然没有意义(会造成栈溢出)
//递归的执行过程:
let i = 0;
function test(){
i++;
console.log('哈哈'+i);
if(i<3){
test();
}
console.log('呵呵'+i);
}
test();
//执行结果:
//哈哈1
//哈哈2
//哈哈3
//呵呵3
//呵呵3
//呵呵3
2.2 递归案例
1.用递归求1-n之间的整数累加和
//分析:
//假如n是5.
//1+2+3+4 +5
//1+2+3 +4
//1+2 +3
//1 +2
//规律: 如果要求1-n之间的整数累加和,先求1-(n-1)之间的整数累加和,再加n本身.
//假设我们写了一个函数getSum,就是用来求1-n之间的整数累加和的.
//那我们现在调用这个getSum函数求1-5之间的整数和,那在求的过程中,再去调用他求1-4的整数累加和.
//那这就是在函数内部调用函数自己,那这不就是递归吗?
function getSum(n){
if(n == 1){
return 1;
}
return getSum(n-1) + n;
}
console.log(getSum(5));
2.递归求斐波那契数列第n位是多少
//2. 用递归求斐波那契数列中第n位是多少?
//1 1 2 3 5 8 13 21 34 55 89.....
//规律: 除了第一位和第二位,以后每一位都是他前两位的和.
//假设我们写了一个函数getFB,就是用来求第n位是多少的.
//那现在调用这个getFB函数求10位是多少,那在求的过程中就还要去调用这个函数求9位和8位是多少.
//那这不就是在调用函数的时候,调用了函数自己吗? 那这不就是递归吗?
function getFB(n){
if(n == 1 || n == 2){
return 1;
}
return getFB(n-1) + getFB(n-2);
}
console.log(getFB(11));
这个递归求稍后的就会特别卡顿(n=50),因为会重复的调用递归求重复的项,导致第4 5十的时候,函数调用了十几亿次。
递归求斐波那契数列优化
//思路: 不要求这么多重复的项
//做法: 把已经求过的项用对象保存起来,下次如果还要求这个项,直接去保存的对象中取出来用.
let i = 0; //声明一个变量,记录调用函数的次数
let obj = {};
function getFB(n) {
i++;
//先判断一下这个n位以前有没有求过.
if(obj[n] != undefined){
//进到这里来,说明以前求过这个n位,那就直接取出来用.
return obj[n];
}else {
//进到这里来,说明以前没有求过这个n位,那就再求不迟.
if(n == 1 || n == 2){
obj[n] = 1;//把此时求的存起来.
return 1;
}else {
obj[n] = getFB(n-1) + getFB(n-2);//把此时求的存起来.
return obj[n];
}
}
}
console.log(getFB(50),i);
还可以通过闭包优化一下,把定义的空对象放在一个函数内,防止其他操作对修改空对象obj
3.递归遍历整个dom元素
//思路:
//有一个api是可以查所有子代的: children
//可以获取这个元素的所有子代,然后让每一个子代再去查找他们的子代,一直查下去,直到子元素没有子代为止.
//假设我们写了一个函数getHD()就是用来求所有的后代的.
//那我们调用这个getHD方法求他的所有后代的时候, 也要去调用这个方法求他的子代的所有后代.
//那不就是在函数内部调用函数自己吗? 那不就是递归吗?
let arr = [];
function getHD(ele){
//查到ele元素的所有子代.
let children = ele.children;
//遍历所有的子代
for(let i = 0; i<children.length;i++){
let child = children[i]; //一个个的子代.
arr.push(child);//把求出来的子元素存起来.
//每一个子代又要去查找他们各自的后代
getHD(child);
}
}
//1.求id为father的这个div的所有后代.
// let father = document.getElementById('father');
// getHD(father);
// console.log(arr);
//2.遍历整个dom树:拿到页面上所有的标签.
getHD(document);
console.log(arr);
3. 自执行函数补充
自执行函数:函数的自调用
其实就是把函数变成表达式去调用
//1.
// (function test(){
// console.log('我是test函数');
// })();
//2.
// (function(){
// console.log('我是函数1');
// })();
//3.
// (function(){
// console.log('我是函数2');
// }());
//4. 把匿名函数变成一个表达式去调用.
// !function(){
// console.log('函数1');
// }();
// +function(){
// console.log('函数2');
// }();
// ~function(){
// console.log('函数3');
// }();
除了常见的(函数)(),(函数()) !函数() +函数() ~函数() 都可以实现自执行函数,牢记:自执行函数前面必须要带结束符(😉,不然前面的内容会和函数连在一起被解析。
总结
递归?递归?!递归!!!
希望每个人都能:有人爱 有事做 有所期盼。加油