目录
一、闭包
闭包:就是一个函数可以访问另一个函数内的局部变量,这个访问者就被称呼为闭包
作用:就是延伸了变量的作用范围
1:闭包的两个方式
闭包的方式1:
function fn() {
let num = 10;
function f2() { //f2是内部函数,一个闭包
console.log(num); //使用了父函数中声明的变量 10
}
f2();
}
fn();
执行思路:fn-f2-把num变量被f2函数调用,输出log 结果为10
闭包的方式2:
function fun() {
let num = 20;
// function fun1() {
// console.log(num); //这是一个闭包函数 ,调用了另一个函数的局部变量 20
// }
// return fun1; //返回的是一个函数 fun1
//或者这样的更简洁
return function () {
console.log(num);
}
}
let f = fun(); //由于调用fun函数,返回的是一个函数,用f来接收这个函数,并且调用f函数
f();
2:闭包案例之循环点击事件
需求:利用闭包的方式,实现点击li输出索引号
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
let lis = document.querySelectorAll("ul li");
方式1:闭包的方式
for (var i = 0; i < lis.length; i++) {
//利用for循环创建3个立即执行函数 可以把i当做参数传递进去 因为i不在函数里面属于当前的i
(function (i) {
lis[i].addEventListener("click", function () {
console.log(i);
})
})(i)
}
方式2:forEach方法
lis.forEach(function (v, i) {
lis[i].addEventListener("click", function () {
console.log(i);
})
})
3:闭包案例之定时器
需求:点击小li,2秒过后,打印对应的索引号
用到定时器
let lis = document.querySelectorAll("ul li");
1:用闭包的方式
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].addEventListener("click", function () {
setTimeout(function () {
console.log(i);
}, 2000)
})
})(i)
}
2:用forEach方法
lis.forEach(function (v, i) {
lis[i].addEventListener("click", function () {
setTimeout(function () {
console.log(i);
}, 2000)
})
})
4:闭包案例之简单的打车案例
需求:
起步价13(3公里内),之后没加一公里就+5,用户输入输入公里数就可以计算打车价格
如果拥堵的情况下,那么总价格加上10
思路:在立即执行函数里面,返回两个函数,分为正常的,还有拥堵的打车函数
在拥堵的打车函数里面,需要判断是否拥堵,利用flag当做开关
var car = (function () {
let start = 13;
let total = 0;
return {
price: function (n) {
if (n > 3) {
total = start + (n - 3) * 5;
} else {
total = start;
}
return total;
},
yd: function (flag) {
return flag ? total + 10 : total;
}
}
})()
console.log(car.price(5)); //23
console.log(car.yd(true)); //33
console.log(car.price(1)); //13
console.log(car.yd(false)); //13
5:思考题
//思考题1:
var name = "The Window";
var Object = {
name: "My Object",
getNameFunc: function () {
return function () {
return this.name;
}
}
}
// console.log(Object.getNameFunc()());
//解析
// var f = object.getNameFunc();
// // 类似于:
// f = function () {
// return this.name;
// }
// f(); //最后输出的是f()函数
// f()又类似于
// function(){}() //立即执行函数 其中的this执行window
//思考2:
var name = "The Window";
var object = {
name: "The Object",
getNameFunc: function () {
var that = this; //这边函数中this指向是指向调用者
return function () {
return that.name;
}
}
}
console.log(object.getNameFunc()()); //The Object
/*
解析:
// object.getNameFunc()() 可以拆分为下面两个,一个f函数,还有调用了f函数
var f = object.getNameFunc(); //函数调用者为object
// 而f函数又类似于
f = function () { //由于函数调用者为object,所以输出的是object
return that.name;
}
f();
*/
二、递归
递归:就是在函数内部调用本身,就是递归函数
注意点:使用递归容易发生栈溢出错误(stack overflow),所以必须添加退出条件
//需求:我要打印6句话
let num = 1;
function fn() {
console.log("我要打印6句话");
if (num == 6) {
return; //递归需要添加退出条件,否则变成了死递归
}
num++;
fn();
}
fn();
/*
执行流程:
外面fn调用函数---先打印--判断条件--num++---递归 到了num为6的时候,就直接退出,这类似while但是比它更香
*/
(1):递归求n!
需求:求123*…n的阶乘
思路:判断条件是当等于1的时候,返回1并且退出 返回的是:最后一项和最后两项相* (最后一项可以作为递归)
let n = prompt("请输入n的值");
function fn(n) {
if (n == 1) {
return 1;
}
return n * fn(n - 1);
}
fn(n);
console.log(fn(n));
(2):利用递归求斐波那契数列(兔子序列) 1,1,2,3,5,8,13,21
需求:利用递归求斐波那契数列(兔子序列) 1,1,2,3,5,8,13,21
思路: 判断条件是:当n输入为小于等于2的时候,就返回1并且退出,规律:前面两项相加为第三项结果(因此可以这两项都为递归调用)
let n = prompt("请输入n的值:")
function fn(n) {
if (n <= 2) {
return 1;
}
return fn(n - 1) + fn(n - 2);
}
console.log(fn(n));