函数高级
1.回调函数 callback
-
异步:做一个操作的时候,可以去做其他的事情 定时器
-
同步:做一个操作的时候,其他的都只能等待 alert for
//- 异步:做一个操作的时候,可以去做其他的事情 定时器 console.log(1); setTimeout(function () { console.log("定时器"); }, 2000); console.log(2); // - 同步:做一个操作的时候,其他的都只能等待 alert for alert(1); alert(2);
-
回调函数:作为参数传递给其他函数调用函数,是解决异步操作的有效途径
-
调用:等某个动作或某个行为执行完以后调用
//- 回调函数:将函数作为参数传递给其他函数调用,解决异步操作的有效途径,等某个动作或某个行为执行完以后调用 setTimeout(fun, 3000); function fun() { console.log("回调函数"); }
2.匿名函数自执行(IIFE)
2.1 函数的种类
-
匿名函数:没有名字的函数
-
学过的函数类型
//1.普通函数 function fun1(){ console.log("普通函数"); } //2.表达式函数 var fun2 = function (){ console.log("表达式函数"); } fun2(); //3.事件处理函数 document.onclick = function(){ console.log("事件处理函数"); } //4.回调函数 setTimeout(function(){ console.log("回调函数");},2000); //5.匿名函数 function (){ console.log("匿名函数"); }
2.2 匿名函数自执行
-
匿名函数:没有名字的函数
//2.匿名函数自执行 (函数的声明)(调用) (function () { console.log("匿名函数"); })();
-
匿名函数传参
//3.函数传参 (function (a, b) { var c = a + b; console.log(c); })(10, 20);
-
匿名函数返回值
//4.返回值 var s = (function (a, b) { var c = a + b; return c; })(10, 20); console.log(s);
2.3 特点和使用场景
-
使用场景
-
在外部js文件的开头添加一个匿名函数自执行
-
在不影响其他代码的基础上添加功能
-
闭包中使用
-
-
优点:
-
避免全局污染,( 全局污染(大量使用全局变量,全局变量可以被任意的修改)
-
完美的嵌入代码
-
-
注意:再写匿名函数的时候前面加;
;(function(){ console.log("匿名1"); })() ;(function(){ console.log("匿名2"); })()
3.闭包函数(Closure)
3.1 回顾
-
局部变量特性
//局部变量、函数的特性 function fun(){ var a = 10; //局部变量,外界不能使用,除了函数的括号就会被销毁 console.log(a++); } fun(); fun();
3.1 闭包基础知识
-
闭包的概念:能访问其他函数内部变量的函数(函数套函数,内部函数可以访问外部函数变量)
-
特性:延伸变量的使用范围,闭包中使用的变量会一直存储在内存中,类似全局变量,避免污染
-
缺点:可能造成内存泄漏, 慎用,不用的时候释放掉, f = null;
//1.闭包:能访问其他函数内部变量的函数(函数套函数,内部函数可以使用外部函数变量) function outer(){ var s = 100; function inner(){ console.log(s); //100 } inner(); } outer(); //2.完整的闭包 function wai(){ var c = 10; return function (){ console.log(c++); } } var inn = wai(); //inn = function(){console.log(c++);} inn(); //10 inn(); //11 inn(); //12 inn(); //13
-
每一次闭包都是独立的模块,相互没有影响
//2.完整的闭包 function wai(){ var c = 10; return function (){ console.log(c++); } } var inn = wai(); //inn = function(){console.log(c++);} inn(); //10 inn(); //11 inn(); //12 inn(); //13 //每一次闭包都是独立的模块,相互没有影响 var inn2 = wai(); //inn2 = function(){console.log(c++);} inn2();
3.2 闭包的使用场景
-
使用场景1:使用函数内部变量
//1.使用函数内部的变量 function outer() { var a = 10; return function () { console.log(a++); } }
-
使用场景2:解决全局作用域问题
//2.解决全局作用域问题 var arr = []; for (var i = 0; i < 5; i++) { arr.push(function () { console.log(i); }) } //代码执行后的结果是? //arr[fun,fun,fun,fun,fun] i = 5,条件不成立,结束循环 // arr[3]() = function(){console.log(i)} arr[3](); //5 arr[2](); //5 //使用闭包修改 var arr1 = []; for (var i = 0; i < 5; i++) { (function (i) { //var i; arr1.push(function () { console.log(i); }) })(i); } arr1[3](); //3 arr1[2](); //2
-
使用场景3:私有属性
//3.私有属性 (idCard外界不可见,可以设置和获取) function Person() { //this.idCard = "431102200312124568"; var idCard = "431102200312124568"; return { "getId": function () { return idCard; }, "setId":function(id){ idCard = id; } } } var p = new Person(); // p = function(){ return idCard;} console.log(p); //获取id console.log(p.getId()); //431102200312124568 //设置id p.setId("431102200412122222"); console.log(p.getId()); //431102200412122222
-
使用场景4:防抖和节流
3.3 闭包面试题
-
面试题1:代码运行完后的结果是,怎么修改
//1.面试题1:i是全局变量, 变成局部变量 var arr = []; for (var i = 0; i < 5; i++) { (function (i) { //var i arr.push(function () { console.log(i); }) })(i); } arr[3](); //3 arr[2](); //2
-
面试题2:使用闭包模拟私有变量
//2.闭包面试题:使用闭包模拟私有属性 id外界不可见 function Person() { // this.idCard = "431102200312124568"; //设置成局部变量,外面就看不到了,但是出了函数括号就会被销毁 var idCard = "431102200312124568"; //形成闭包,闭包中使用的变量会一直存储在内存中,不会被销毁 return { "getId":function(){ //获取id的函数 return idCard; }, "setId":function(id){ //设置id的函数 idCard = id; } } } var p = new Person(); console.log(p); //{getId: ƒ, setId: ƒ} console.log(p.getId()); p.setId("431102200312124560");
-
面试题3:代码执行后的结果是
//3.闭包面试3: 代码执行后的结果 function fun(n,o){ console.log(o); return { "fun":function(m){ return fun(m,n); } } } /* var a = fun(0,undefined) 闭包中使用的变量会一直存储在内存中 n = 0 a = { "fun":function(m){ return fun(m,n); } } a.fun(1) //m = 1; fun(1,0) a.fun(2) //m = 2; fun(2,0) a.fun(3) //m = 3; fun(2,0) */ var a = fun(0); //undefined a.fun(1); //0 a.fun(2); //0 a.fun(3); //0
-
面试题4:代码执行后的结果是
//4.闭包面试4:看代码写程序 for(var i = 0;i<5;i++){ setTimeout(function(){ console.log(new Date(),i); //Thu Aug 26 2021 15:03:50 GMT+0800 (中国标准时间) 5 },1000); } console.log(new Date(),i); //Thu Aug 26 2021 15:03:49 GMT+0800 (中国标准时间) 5
4.递归函数
-
递归:函数内部调用函数本身
-
和循环的区别:循环无限执行会变成死循环,卡死,递归有最大堆栈调用次数,超过会报错
4.1 阶乘
-
递归的实现过程
-
找规律
-
递:函数内部调用函数本身
-
归:在函数的开头,结束递归的执行
//1.先找规律 //6! 阶乘 6*5*4*3*2*1 //6! 6 * 5! //5! 5 * 4! //4! 4 * 3! //n! = n * (n-1) //2.递://超出最大调用堆栈大小 // function jc(n) { // return n * jc(n-1); // } // jc(6); //3.归(一定要结束) function jc(n){ //归:一定是函数的开头 if(n == 1) return 1 //递 return n * jc(n-1); // return n * jc(n-1); // // 6 * jc(5) 6*5*4*3*2*1 // //5 * jc(4) 5*4*3*2*1 // //4 * jc(3) 4*3*2*1 // //3 * jc(2) 3*2*1 // //2 * jc(1) 1 // //1 } console.log(jc(10));
-
4.2 斐波那契数列
-
数列特点:1,1,2,3,5,8,13,21,34 。。。
-
数列规律:f(n) = f(n-1) + f(n-2)
//1.找规律 1 1 2 3 5 8 13 21 34 //f(6) = f(5) + f(4) //f(5) = f(4) + f(3) //2.递 // function rabbit(n){ // return rabbit(n-1) + rabbit(n-2); // } // console.log(rabbit(6)); //8 //3.归 function rabbit(n){ if(n == 1 || n == 2) return 1; return rabbit(n-1) + rabbit(n-2); } console.log(rabbit(6)); //8 console.log(rabbit(9)); //34
4.3 快速排序
-
实现思路
arr.length <=1 return arr; var arr = [2,7,1,5,4,6,8] 1.获取数组中间的元素 (从数组中将中间元素删除) var middle = arr.aplice(arr.length/2,1)[0]; 2.创建两个空数组,left,right 3.循环数组和中间值进行比较,比中间值小的放left,比中间值大的放后面 middle = 5 left = [2,1,4] right = [7,6,8] 4.组合 return quick(left).concat(middle,quick(right))
-
代码实现
function quick(arr) { //5.结束递归的条件 if(arr.length<=1) return arr; //1.找中间值 (删除掉中间值) var middle = arr.splice(Math.floor(arr.length / 2), 1)[0]; //2.创建两个空数组 var left = []; var right = []; //3.循环数组和中间值进行比较,比中间值小的放前面,大的放后面 for(var i = 0;i<arr.length;i++){ if(arr[i] < middle){ left.push(arr[i]); }else{ right.push(arr[i]); } } //4.组合 return quick(left).concat(middle,quick(right)); } var arr = [2, 7, 1, 5, 4, 6, 8] console.log(quick(arr));;
5.防抖与节流
-
作用:用于处理性能优化
5.1 防抖(debounce)
-
防抖:在规定的事件内触发一次,开启一个定时器,延迟某个时间执行(500ms),如果在这500ms内,有再次触发事件,重新计数
-
防抖的目的:让高频发的事件,在规定时间内,触发1次
-
实现思路: 开一个定时器,事件延迟某个时间(30ms),如果在这30ms内再次触发整个事件,重新开始计数
-
基础实现
//2.在div中移动,数字+1 oDiv.onmousemove = debounce(fun,30); function fun() { oDiv.innerText++; } //3.防抖:事件发生的时候开启定时器,开始计数(30),如果在计数范围内又触发了事件,那就重新开始计数 var timer; function change() { //判断,如果之前已经开启了定时器,就清除掉之前的 if (timer) { clearTimeout(timer) } timer = setTimeout(function () { fun(); }, 30); }
-
封装
function debounce(fun,wait) { //fun:事件处理函数, wait:延迟事件 var timer; //维护全局纯净,借助闭包来实现 return function () { if (timer) { //timer有值为真,这个事件已经触发了一次,重新开始计数 clearTimeout(timer); } timer = setTimeout(function () { fun(); }, wait); } }
-
5.2 节流
-
在规定的事件内触发一次,开启定时器,延迟某个时间执行一次,如果在等待的这个时间内再处触发事件,不处理,控制事件的执行频率