函數定義/函數聲明
1. 自定義函數(命名式函數)
function fn() {}
2. 函數表達式(匿名函數)
var fun = function(){}
3. new Function('參數1', '參數2', ... ,'函數體')
// 參數:空;函數體:console.log(123)
var f = new Function('console.log(123)');
f(); // 函數調用
// 參數:a, b;函數體:console.log(123)
var f = new Function('a', 'b', 'console.log(a + b)');
f(1, 2);
4. 所有函數都是Function的實例(對象)
console.dir(f);
console.log(f instanceof Object); // true
函數的定義方式
<script>
var f = new Function('a', 'b', 'console.log(a + b)');
f(1, 2); // result: 3
</script>
- console.log(f instanceof Object); // true
-
console.log(f.prototype);
-
console.log(f.__proto__);
函數調用方式
1. 普通函數
function fn() {
console.log(123);
}
fn(); // 調用方式一
fn.call(); // 調用方式二
2. 對象的方法
var o = {
sayHi: function(){
console.log('hello, world');
}
}
o.sayHi(); // 函數調用(對象.方法名)
3. 構造函數
function Start() {};
new Start(); // 函數調用(實例化一個對象)
4. 綁定事件函數(匿名函數)
btn.onclick = function() {}; // 點擊按鈕時調用函數
5. 定時器函數
setInterval(function() {}, 1000) // 每隔1s執行一次
6. 立即執行函數 // 自動調用(自執行函數)
(function(){})() // 創建閉包
(function(){
console.log('hello, world');
})()
匿名函數的用法(2種方式)
- 賦值函數
- 自執行函數
自執行函數
var b = function() {}; //聲明
b(); // 調用
自執行函數 = 聲明 + 調用
(b)();
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS Test02...</title>
</head>
<body>
<script>
(function() {
console.log(123);
})()
</script>
</body>
</html>
瀏覽器加載index.html,log窗口輸出123。函數沒有被調用,直接運行。自執行函數可直接使用,可用作創建命名空間、插件等。
自執行函數的調用方式常見的有三種:
//1.方式一
(function(){
document.write('wo hao');
})();
//2.方式二
(function(){
document.write('hello');
}());
//3.方式三
[function(){
document.write('world');
}()];
參考鏈接:https://blog.csdn.net/weixin_44388523/article/details/86514627
實例:(知識點:自執行函數)
<script>
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i*1000);
}
</script>
output:
5
5
5
5
5
原因:js中沒有塊級作用域,只有函數作用域。
1.首先執行for循環,設置5個定時器,定時器分別是0,1s,2s,3s,4s。
2.執行過程中需要打印i,i在for循環中定義。for循環執行結束後,i值爲5。每次打印值爲5
通過自執行函數解決以上問題
<script>
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i*1000);
})(i);
}
</script>
output:
0
1
2
3
4
方法2: ES6 使用let,塊級作用域。i只能在for循環內部使用,for循環外部無法使用i,i會依次增加。
<script>
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i*1000);
}
</script>
output:
0
1
2
3
4
擴展:去掉function的參數i
console.log(i);向父元素尋找i,未找到。向for循環中找i,結果i爲5。
實例2(知識點:自執行函數)
// test.js
for (var i = 0; i < 5; i++) {
setTimeout((function(i){
console.log(i);
})(i), i*1000);
}
在node環境中運行test.js,報錯:
timers.js:390 throw new ERR_INVALID_CALLBACK(); ^ TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
原因:
node環境中,setTimeout函數第一參數必須是函數。自執行函數運行的結果不是函數。
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS Test02...</title>
</head>
<body>
<script>
for (var i = 0; i < 5; i++) {
setTimeout((function(i){
console.log(i);
})(i), i*1000);
}
</script>
</body>
</html>
瀏覽器打開index.html,運行結果如下:
0
1
2
3
4
瀏覽器中代碼解析如下:
setTimeout((function(i){ //沒有返回值
console.log(i);
})(i), i*1000);
相當於
setTimeout(unddefined,i*1000);
不存在異步操作。
實例3(知識點:異步調用、Promise)
setTimeout(function() {
console.log(1);
}, 0);
new Promise( r => {
console.log(2);
r();
console.log(3);
}).then( data => {
console.log(4);
})
console.log(5);
Event Loop 機制
主線程:2 3 5 4 1
異步
宏任務 setTimeout setInterval requestAnimationFrame
微任務 Promise mutationObserver interSectionObeser
1. setTimeout進入隊列任務
2. r() 進入任務隊列
3. 主線程執行完畢,依次執行微任務,宏任務