JavaScript高级 - 4 - 闭包和递归
1. 闭包
1.1 变量的作用域复习
变量根据作用域的不同分为两种:全局变量和局部变量。
- 函数内部可以使用全局变量。
- 函数外部不可以使用局部变量。
- 当函数执行完毕,本作用域内的局部变量会销毁。
1.2 什么是闭包
闭包(closure)指有权访问另一个函数作用域中变量的函数。简单理解就是 ,一个函数可以访问另外一个函数内部的局部变量。
// fn1 就是一个闭包
function fn1(){
var num = 1;
function fn2(){
这里可以访问到 fn1 中的变量 num
console.log(num);
}
}
fn1();
out:
1
1.3 闭包的作用
作用:延伸变量的作用范围。
function fn1() {
var num = 10;
function fn2() {
console.log(num);
}
return fn2;
}
var f = fn1(); // 把 fn2 赋值给了 f
f(); // 相当于调用 fn2
out:
10
1.4 闭包的案例
-
利用闭包的方式得到当前 li 的索引号
for (var i = 0; i < lis.length; i++) { // 利用for循环创建了4个立即执行函数 // 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量 (function(i) { lis[i].onclick = function() { console.log(i); } })(i); }
-
等待 3 秒钟之后,打印所有 li 元素的内容
for (var i = 0; i < lis.length; i++) { (function(i) { setTimeout(function() { console.log(lis[i].innerHTML); }, 3000) })(i); }
-
计算打车价格
/*需求分析 打车起步价13(3公里内), 之后每多一公里增加 5块钱. 用户输入公里数就可以计算打车价格 如果有拥堵情况,总价格多收取10块钱拥堵费*/ var car = (function() { var start = 13; // 起步价 局部变量 var total = 0; // 总价 局部变量 return { // 正常的总价 price: function(n) { if (n <= 3) { total = start; } else { total = start + (n - 3) * 5 } return total; }, // 拥堵之后的费用 yd: function(flag) { return flag ? total + 10 : total; } } })(); console.log(car.price(5)); // 23 console.log(car.yd(true)); // 33
-
对象方法中使用闭包
// 1. 不使用闭包 var name = "The Window"; var object = { name: "My Object"; getNameFunc: function() { return function() { // 这里的this实际指向 window 对象 return this.name; }; } }; console.log(object.getNameFunc()()); out: The Window ----------------------------------------------------------------------------------- // 2. 使用闭包 var name = "The Window"; var object = { name: "My Object"; getNameFunc: function() { // 将object对象对应的this赋值给that var that = this; function printName() { return that.name; }; return printName; } }; console.log(object.getNameFunc()()) out: My Object
2. 递归
2.1 什么是递归
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数
注意: 递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。
2.2 利用递归求1~n的阶乘
//利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n
function fn(n) {
if (n == 1) { //结束条件
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
out:
6
2.3 利用递归求斐波那契数列
// 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值
function fb(n) {
if (n === 1 || n === 2) {
return 1;
}
return fb(n - 1) + fb(n - 2);
}
console.log(fb(3));
2.4 利用递归遍历数据
// 我们想要输入id号,就可以返回的数据对象
var data = [{
id: 1,
name: '家电',
goods: [{
id: 11,
gname: '冰箱',
goods: [{
id: 111,
gname: '海尔'
}, {
id: 112,
gname: '美的'
}]
}, {
id: 12,
gname: '洗衣机'
}]
}, {
id: 2,
name: '服饰'
}];
function getID(data, id) {
var o = {};
// 1. 利用 forEach 去遍历里面的每一个对象
data.forEach(function(item) {
// console.log(item); // 2个数组元素
if (item.id == id) {
// console.log(item);
o = item;
return o;
// 2. 我们想要得里层的数据 11 12 可以利用递归函数
// 3. 里面应该有goods这个数组并且数组的长度不为 0
} else if (item.goods && item.goods.length > 0) {
o = getID(item.goods, id);
}
});
return o;
}
getID(data, 111);
out:
{id: 111, gname: "海尔"}