四、高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。
以上情况 fn 函数就是一个高阶函数
函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。 最典型的就是作为回调函数。 同理函数也可以作为返回值传递回来
五、闭包
1、变量作用域
变量根据作用域的不同分为两种:全局变量 和 局部变量。
- 函数内部可以使用全局变量
- 函数外部不可以使用局部变量
- 当函数执行完毕,本作用域内的局部变量会销毁
2、 什么是闭包
闭包(closure) 指有权访问另一个函数作用域中变量的函数。 ----- JavaScript 高级程序设计
简单理解就是,一个作用域可以访问另外一个函数内部的局部变量。
代码:
<script> function fn1(){ // fn1 就是闭包函数 var num = 10; function fn2(){ console.log(num); // 10 } fn2() } fn1(); </script>
3、在 chrome 中调试闭包
- 打开浏览器,按 F12 键启动 chrome 调试工具
- 设置断点
- 找到 Scope 选项(Scope 作用域的意思)
- 当我们重新刷新页面,会进入断点调试,Scope 里面会有两个参数(global 全局作用域、local 局部作用域)。
- 当执行到 fn2() 时,Scope 里面会多一个 Closure 参数 ,这就表明产生了闭包。
4、闭包的作用
闭包作用:延伸变量的作用范围
代码:
<script> function fn() { var num = 10; return function { console.log(num); // 10 } } var f = fn(); f(); </script>
5、闭包案例
- 循环注册点击事件
for (var i = 0; i < lis.length; i++) { // 利用for循环创建了4个立即执行函数 // 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变 量 (function(i) { lis[i].onclick = function() { console.log(i); } })(i); }
- 循环中的 setTimeout()
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、什么是递归?
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
简单理解:函数内部自己调用自己,这个函数就是递归函数
递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return
2、利用递归求数学题
- 求 1 * 2 *3 … * n 阶乘
代码:
function fn(n) { if (n == 1) return 1; return n * fn(n - 1); } console.log(fn(4));
- 求斐波那契数列
代码:
function fb(n) { if (n === 1 || n === 2) { return 1; } return fb(n - 1) + fb(n - 2); } console.log(fb(3)); // 2 console.log(fb(6)); // 8
- 根据id返回对应的数据对象
var data = [{ id: 1, name: '家电', goods: [{ id: 11, gname: '冰箱', goods: [{ id: 111, gname: '海尔' }, { id: 112, gname: '美的' }, ] }, { id: 12, gname: '洗衣机' }] }, { id: 2, name: '服饰' }]; // 我们想要做输入id号,就可以返回的数据对象 // 1. 利用 forEach 去遍历里面的每一个对象 function getID(json, id) { var o = {}; json.forEach(function(item) { if (item.id == id) { o = item; return o; // 2. 我们想要得里层的数据 11 12 可以利用递归函数 } else if (item.goods && item.goods.length > 0) { o = getID(item.goods, id); } }); return o; } console.log(getID(data, 1)); console.log(getID(data, 2)); console.log(getID(data, 11)); console.log(getID(data, 12)); console.log(getID(data, 111));
3、浅拷贝和深拷贝
浅拷贝只是拷贝一层,更深层次的对象级别的只拷贝引用。深拷贝会拷贝多层,每一级的数据都会拷贝。
- 浅拷贝
代码:
var obj = { id: 1, name: "章三", msg: { age: 18, }, }; var o = {}; // 方式一: for (const key in obj) { o[key] = obj[key]; } // 方式二: // 建议使用 Es6 中新增的 Object.assign() 方法实现浅拷贝 Object.assign(o, obj); obj.msg.age = 20; console.log(obj); console.log(o);
深拷贝
代码:
function deepCopy(newobj, oldobj) { for (const key in oldobj) { // 获取对象中的属性 var item = oldobj[key]; if (item instanceof Array) { // 判断是否为数组 newobj[key] = []; deepCopy(newobj[key], item); } else if (item instanceof Object) { // 判断是否为对象 newobj[key] = {}; deepCopy(newobj[key], item); } else { // 判断是否为普通值 newobj[key] = item; } } } deepCopy(o, obj); obj.msg.age = 20; console.log(obj); console.log(o);