1.高階函數
高階函數是對其他函數進行操作的函數,它接收函數作爲參數或將函數作爲返回值輸出。
此時fn就是一個高階函數。
函數也是一種數據類型,同時可以作爲參數,傳遞給另外一個參數使用,最典型的就是作爲回調函數
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div></div>
<script>
//高階函數 函數可以作爲參數傳遞
function fn(a, b, callback) {
console.log(a + b);
callback && callback();
}
fn(1, 2, function() {
console.log('我是最後調用的');
});
</script>
</body>
</html>
2.閉包
2.1變量作用域
變量根據作用域的不同可分爲兩種:全局變量和局部變量
1.函數內部可以使用全局變量
2.函數外部不可以使用局部變量
3.當函數執行完畢,本作用域內的局部變量會銷燬
2.2什麼是閉包
閉包(closure)指有權限訪問另一個函數作用域中變量的函數。
簡單理解就是,一個作用域可以訪問另外一個函數內部的局部變量
2.4閉包的作用
延伸了變量的使用範圍
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//閉包(closure)指有權限訪問另一個函數作用域中變量的函數
//閉包:我們fun這個函數作用域 訪問了另外一個函數fn裏面的局部變量 num
//我們fn 外面的作用域可以訪問fn 內部的局部變量
//閉包的主要作用:延伸了變量的作用範圍
function fn() {
var num = 10;
// function fun() {
// console.log(num);
// }
// fun();
return function() {
console.log(num);
};
}
var f = fn();
f();
//類似於
// var f = function fun() {
// console.log(num);
// }
</script>
</body>
</html>
閉包的案例一:點擊li,輸出當前li索引號
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul class="nav">
<li>巧克力</li>
<li>棒棒糖</li>
<li>牛奶</li>
<li>蛋糕</li>
</ul>
<script>
//1.我們可以利用動態添加屬性的方式
var lis = document.querySelector('.nav').querySelectorAll('li');
// for (var i = 0; i < lis.length; i++) {
// lis[i].index = i;
// lis[i].onclick = function() {
// console.log(this.index);
// }
// }
//2.利用閉包的方式得到當前li的索引號
for (var i = 0; i < lis.length; i++) {
//利用for循環創建了4個立即執行函數
//立即執行函數也稱爲小閉包 因爲立即執行函數裏面任意一個函數都可以使用它的i這個變量
(function(i) {
// console.log(i);
lis[i].onclick = function() {
console.log(i);
}
})(i);
}
</script>
</body>
</html>
閉包的案例二:3秒之後,打印所有的li元素的內容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul class="nav">
<li>巧克力</li>
<li>棒棒糖</li>
<li>牛奶</li>
<li>蛋糕</li>
</ul>
<script>
//閉包應用——3秒之後,打印所有的li元素的內容
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(i);
}
</script>
</body>
</html>
閉包的案例三:計算打車價格
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//閉包運用:計算打車的價格
//打車起步價13(3公里以內) 之後每多一公里增加5塊錢 用戶輸入公里數就可以計算打車的價格
//如果有擁堵的情況 總價格多收取10塊錢擁堵費
// function fn() {
// }
// fn;
var car = (function fn() {
var start = 13; //起步價 局部變量
var totle = 0; //總價 局部變量
return {
price: function(n) {
if (n <= 3) {
totle = start;
} else {
totle = start + (n - 3) * 5;
}
return totle;
},
yd: function(flag) {
return flag ? totle + 10 : totle
} //擁堵之後的費用
}
})();
console.log(car.price(5)); //23
console.log(car.yd(true)); //33
console.log(car.price(1)); //13
console.log(car.yd(true)); //23
</script>
</body>
</html>
思考題:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//思考題一:
// var name = "The window";
// var object = {
// name: "My Object",
// getNameFunc: function() {
// return function() {
// return this.name;
// };
// }
// };
// console.log(object.getNameFunc()()); // 輸出結果The window
//解析:
// var f = object.getNameFunc();
//類似於
// var f = function() {
// return this.name;
// }
// f();
//思考題二:
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()()); //輸出的是My Object
// var f = object.getNameFunc();
// var f = function() {
// return that.name;
// };
// f();
</script>
</body>
</html>
3.遞歸
3.1什麼是遞歸?
如果一個函數在內部可以調用其本身,那麼這個函數就是遞歸函數,簡單理解:函數內部自己調用自己,這個函數就是遞歸函數。
遞歸函數的作用域和循環效果一樣,由於遞歸很容易發生“棧溢出”錯誤,所以必須要加退出條件return
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//遞歸函數:函數內部自己調用自己, 這個函數就是遞歸函數
var num = 1;
function fn() {
console.log('我要打印六句話');
if (num == 6) {
return; //遞歸裏面必須加退出條件
}
num++;
fn();
}
fn();
</script>
</body>
</html>
遞歸求階乘:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//利用遞歸函數求n的階乘
function fn(n) {
if (n === 1) {
return 1;
}
return n * fn(n - 1);
}
fn(3);
console.log(fn(3));
console.log(fn(4));
//詳細思路 假如用戶輸入的是3
//return 3 * fn(2)
//return 3 * (2 * 1);
</script>
</body>
</html>
遞歸求斐波那契數列:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//利用遞歸函數求斐波那契數列(兔子序列)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));
console.log(fb(1));
console.log(fb(6));
</script>
</body>
</html>
通過ID號得到相應的數據對象:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
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) {
// console.log(item); //兩個數組元素
if (item.id === id) {
// console.log(item);
o = item;
return item;
//2.我們想要得到裏層的數據11 12 可以利用遞歸
//裏面應該要有goods數組 並且數組的長度不爲0
} 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));
</script>
</body>
</html>
3.2淺拷貝和深拷貝
1.淺拷貝只是拷貝一層,更深層次對象級別的只拷貝引用
2.深拷貝拷貝多層,每一級的數據都會拷貝
3.Object.assign(target,……sources) ES6新增方法淺拷貝
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 1.淺拷貝只是拷貝一層,更深層次對象級別的只拷貝引用
// 2.深拷貝拷貝多層,每一級的數據都會拷貝
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
}
};
var o = {};
for (var k in obj) {
//k是屬性名 obj[k]是屬性值
o[k] = obj[k];
}
// console.log(o);
// o.msg.age = 20;
// console.log(obj);
console.log('-----------------------');
Object.assign(o, obj);
console.log(o);
o.msg.age = 20;
console.log(obj);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//深拷貝拷貝多層 每一級別的數據都會拷貝
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
}
};
var o = {};
//封裝函數
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
//判斷我們的屬性值屬於哪種數據類型
//1.獲取屬性值 old[k]
var item = oldobj[k];
//2.判斷這個值是否是數組
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item);
} else if (item instanceof Object) {
//3.判斷這個值是否是對象
newobj[k] = {};
deepCopy(newobj[k], item);
} else {
//4.屬於簡單數據類型
newobj[k] = item;
}
}
}
deepCopy(o, obj);
console.log(o);
o.msg.age = 20;
console.log(obj);
</script>
</body>
</html>