Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
現(xiàn)代前端開發(fā)中,回調(diào)函數(shù)是一種非常重要的概念。它們?cè)试S我們?cè)谔囟ǖ臅r(shí)刻執(zhí)行代碼,常用于處理異步操作,例如事件監(jiān)聽、網(wǎng)絡(luò)請(qǐng)求等。在本文中,我將通過幾個(gè)例子來深入探討回調(diào)函數(shù)的使用。
回調(diào)函數(shù)(Callback)是一個(gè)作為參數(shù)傳遞給另一個(gè)函數(shù)的函數(shù),這個(gè)回調(diào)函數(shù)將在外部函數(shù)的內(nèi)部被執(zhí)行。在JavaScript中,由于其事件驅(qū)動(dòng)和異步的特性,回調(diào)函數(shù)應(yīng)用非常廣泛。
下面是一個(gè)簡(jiǎn)單的HTML按鈕點(diǎn)擊事件的例子,我們將為按鈕元素添加一個(gè)點(diǎn)擊事件監(jiān)聽器,并傳遞一個(gè)回調(diào)函數(shù)來處理點(diǎn)擊事件。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>回調(diào)函數(shù)示例:事件監(jiān)聽</title>
</head>
<body>
<button id="clickMeBtn">點(diǎn)擊我</button>
<script>
document.getElementById('clickMeBtn').addEventListener('click', function() {
alert('按鈕被點(diǎn)擊了!');
});
</script>
</body>
</html>
在這個(gè)例子中,我們定義了一個(gè)匿名函數(shù)作為addEventListener方法的第二個(gè)參數(shù)。當(dāng)用戶點(diǎn)擊按鈕時(shí),這個(gè)匿名函數(shù)就會(huì)被調(diào)用。
異步操作,如網(wǎng)絡(luò)請(qǐng)求,是回調(diào)函數(shù)的另一個(gè)常見用例。以下是使用XMLHttpRequest對(duì)象發(fā)起網(wǎng)絡(luò)請(qǐng)求的示例。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>回調(diào)函數(shù)示例:異步操作</title>
</head>
<body>
<script>
function requestData(url, callback) {
var xhr=new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange=function() {
if (xhr.readyState===4 && xhr.status===200) {
callback(null, xhr.responseText);
} else if (xhr.readyState===4) {
callback(new Error('請(qǐng)求失敗'));
}
};
xhr.send();
}
requestData('https://api.example.com/data', function(error, data) {
if (error) {
console.error('發(fā)生錯(cuò)誤:', error);
} else {
console.log('接收到的數(shù)據(jù):', data);
}
});
</script>
</body>
</html>
在這個(gè)例子中,我們定義了一個(gè)名為requestData的函數(shù),它接收一個(gè)URL和一個(gè)回調(diào)函數(shù)作為參數(shù)。requestData函數(shù)內(nèi)部創(chuàng)建一個(gè)XMLHttpRequest對(duì)象,并在請(qǐng)求完成時(shí)調(diào)用回調(diào)函數(shù),傳遞錯(cuò)誤對(duì)象或響應(yīng)數(shù)據(jù)。
JavaScript定時(shí)器函數(shù)setTimeout和setInterval也使用回調(diào)函數(shù)。以下是一個(gè)使用setTimeout的例子。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>回調(diào)函數(shù)示例:定時(shí)器</title>
</head>
<body>
<script>
function delayLog(message, delay) {
setTimeout(function() {
console.log(message);
}, delay);
}
delayLog('3秒后的消息', 3000);
</script>
</body>
</html>
在這個(gè)例子中,delayLog函數(shù)接收一個(gè)消息和一個(gè)延遲時(shí)間(毫秒)作為參數(shù)。它使用setTimeout來延遲執(zhí)行一個(gè)匿名函數(shù),該函數(shù)將在指定的延遲后輸出消息。
回調(diào)函數(shù)是JavaScript編程的基石之一,它們提供了一種處理異步操作和事件的強(qiáng)大手段。理解并正確使用回調(diào)函數(shù)對(duì)于任何前端工程師來說都是至關(guān)重要的。通過本文的例子,我們可以看到回調(diào)函數(shù)在實(shí)際編程中的多種應(yīng)用場(chǎng)景,希望這些例子能幫助你更好地理解回調(diào)函數(shù)的工作原理和使用方法。
定義:回調(diào)函數(shù)就是一個(gè)通過函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來調(diào)用其所指向的函數(shù)時(shí),我們就說這是回調(diào)函數(shù)。
案例一:定義一個(gè)名為 add 的函數(shù),接收兩個(gè)參數(shù),將這個(gè)兩個(gè)參數(shù)相加,作為返回值。當(dāng)調(diào)用這個(gè)函數(shù)的時(shí)候,就可以得到返回值。
function add(x, y){
return x + y;
};
var result=add(10, 20);
console.log(result);
運(yùn)行結(jié)果:
說明:像上面的 add 函數(shù),可以直接得到返回值。
案例二:定義一個(gè)函數(shù) add,也是接收兩個(gè)參數(shù),在這個(gè) add 函數(shù)中同時(shí)定義了一個(gè) setTimeout 函數(shù),想在一秒鐘之后,返回這兩個(gè)參數(shù)之和。
演示兩種獲取不到的方法:
第一種:在 setTimeout 中返回結(jié)果
function add(x, y){
console.log(1);
setTimeout(function(){
console.log(2);
var result=x + y;
return result;
}, 1000);
console.log(3);
};
console.log(add(10, 20));
運(yùn)行結(jié)果:
說明:當(dāng)輸出調(diào)用 add 函數(shù)的時(shí)候,js 代碼從上到下執(zhí)行,當(dāng)遇到異步的 setTimeout 函數(shù)的時(shí)候,不會(huì)等待異步函數(shù),繼續(xù)往下執(zhí)行,當(dāng)執(zhí)行 console.log(add(10, 20)); 的時(shí)候,函數(shù)沒有返回值,所以打印出 undefined。
第二種:在外層函數(shù)定義一個(gè) result 變量,在 setTimeout 函數(shù)里進(jìn)行賦值。
function add(x, y){
var result=0;
setTimeout(function(){
result=x + y;
}, 1000);
return result;
};
console.log(add(20, 20));
運(yùn)行結(jié)果:
說明:js 代碼按照順序執(zhí)行,當(dāng)執(zhí)行到 setTimeout 的時(shí)候,不會(huì)等待,繼續(xù)往下執(zhí)行,所以 result 還是 0。
function add(x, y, callback){
setTimeout(function(){
var result=x + y;
callback(result);
}, 1000);
};
add(30, 20, function(result){
console.log(result);
});
運(yùn)行結(jié)果:
說明:add 函數(shù)中的 callback,只是起的名稱,意思是 回調(diào)函數(shù),在輸出調(diào)用 add 函數(shù)的時(shí)候,傳了 x,y 和 一個(gè)函數(shù),其實(shí)就是將函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來調(diào)用時(shí),就是去找 function(result){console.log(result)}。所以在調(diào)用add 函數(shù)的時(shí)候,代碼按照正常順序執(zhí)行,過了 1 秒之后,執(zhí)行 setTimeout 里的函數(shù),執(zhí)行到 callback 的時(shí)候,會(huì)去執(zhí)行function(result){console.log(result)}。
路漫漫其修遠(yuǎn)兮,吾將上下而求索
譯文:在追尋真理方面,前方的道路還很漫長,但我將百折不撓,不遺余力地去追求和探索。
如果您有什么好的想法與方法,歡迎在評(píng)論區(qū)留言,我們一起討論~
文已經(jīng)過原作者 dmitripavlutin 授權(quán)翻譯!
回調(diào)函數(shù)是每個(gè) JS 開發(fā)人員都應(yīng)該知道的概念之一。回調(diào)用于數(shù)組,計(jì)時(shí)器函數(shù),promise,事件處理程序等中。
在本文中,會(huì)解釋回調(diào)函數(shù)的概念。另外,還會(huì)幫助智米們區(qū)分兩種回調(diào):同步和異步。
我們編寫一個(gè)問候的函數(shù),首先創(chuàng)建一個(gè)函數(shù)greet(name),該函數(shù)返回歡迎消息:
function greet(name) {
return `Hello, ${name}!`;
}
greet('小智'); // => 'Hello, 小智!'
如果要向一些人問候怎么做?這里,我們可以使用 array.map() 方法:
const persons = ['小智', '王大冶']
const messages = persons.map(greet)
messages // ["Hello, 小智!", "Hello, 王大冶!"]
persons.map(greet)接受person數(shù)組的每一項(xiàng),并使用每一項(xiàng)作為調(diào)用參數(shù)來調(diào)用函數(shù)greet():greet('小智'),greet('王大冶')。
有趣的是persons.map(greet)方法接受greet()函數(shù)作為參數(shù)。這樣做會(huì)使reet()成為回調(diào)函數(shù)。
persons.map(greet)是一個(gè)接受另一個(gè)函數(shù)作為參數(shù)的函數(shù),因此將其命名為高階函數(shù)。
高階函數(shù)承擔(dān)調(diào)用回調(diào)函數(shù)的全部責(zé)任,并為其提供正確的參數(shù)。
在前面的示例中,高階函數(shù)persons.map(greet)負(fù)責(zé)調(diào)用greet()回調(diào)函數(shù),并將數(shù)組的每個(gè)項(xiàng)目作為參數(shù):'小智'和'王大冶'。
我們可以可以自己編寫使用回調(diào)的高階函數(shù)。例如,這里有一個(gè)等價(jià)的array.map()方法
function map(array, callback) {
const mappedArray = [];
for (const item of array) {
mappedArray.push(
callback(item)
);
}
return mappedArray;
}
function greet(name) {
return `Hello, ${name}!`;
}
const persons = ['小智', '王大冶']
const messages = map(persons, greet);
messages // ["Hello, 小智!", "Hello, 王大冶!"]
map(array, callback)是一個(gè)高階函數(shù),因?yàn)樗邮芑卣{(diào)函數(shù)作為參數(shù),然后在它的函數(shù)體內(nèi)部調(diào)用回調(diào)函數(shù):callback(item)。
回調(diào)的調(diào)用方式有兩種:同步和異步回調(diào)。
同步回調(diào)是在使用回調(diào)的高階函數(shù)執(zhí)行期間執(zhí)行的。
換句話說,同步回調(diào)處于阻塞狀態(tài):高階函數(shù)要等到回調(diào)完成執(zhí)行后才能完成其執(zhí)行。
function map(array, callback) {
console.log('map() 開始');
const mappedArray = [];
for (const item of array) { mappedArray.push(callback(item)) }
console.log('map() 完成');
return mappedArray;
}
function greet(name) {
console.log('greet() 被調(diào)用 ');
return `Hello, ${name}!`;
}
const persons = ['小智'];
map(persons, greet);
// map() 開始
// greet() 被調(diào)用
// map() 完成
greet()是一個(gè)同步回調(diào)函數(shù),因?yàn)樗c高階函數(shù)map()同時(shí)執(zhí)行。
很多原生 JavaScript 類型的方法都使用同步回調(diào)。
最常用的是數(shù)組方法,例如array.map(callback),array.forEach(callback),array.find(callback),array.filter(callback),array.reduce(callback, init):
// 數(shù)組上的同步回調(diào)的示例
const persons = ['小智', '前端小智']
persons.forEach(
function callback(name) {
console.log(name);
}
);
// 小智
// 前端小智
const nameStartingA = persons.find(
function callback(name) {
return name[0].toLowerCase() === '小';
}
)
// nameStartingA // 小智
const countStartingA = persons.reduce(
function callback(count, name) {
const startsA = name[0].toLowerCase() === '小';
return startsA ? count + 1 : count;
},
0
);
countStartingA // 1
異步回調(diào)在執(zhí)行高階函數(shù)之后執(zhí)行。
簡(jiǎn)而言之,異步回調(diào)是非阻塞的:高階函數(shù)無需等待回調(diào)即可完成其執(zhí)行,高階函數(shù)可確保稍后在特定事件上執(zhí)行回調(diào)。
在下面的示例中,later()函數(shù)的執(zhí)行延遲為2秒
console.log('setTimeout() 開始')
setTimeout(function later() {
console.log('later() 被調(diào)用')
}, 2000)
console.log('setTimeout() 完成')
// setTimeout() 開始
// setTimeout() 完成
// later() 被調(diào)用(2秒后)
計(jì)時(shí)器函數(shù)的異步回調(diào):
setTimeout(function later() {
console.log('2秒過去了!');
}, 2000);
setInterval(function repeat() {
console.log('每2秒');
}, 2000);
DOM 事件監(jiān)聽器也是異步調(diào)用事件處理函數(shù)(回調(diào)函數(shù)的一種子類型)
const myButton = document.getElementById('myButton');
myButton.addEventListener('click', function handler() {
console.log('我被點(diǎn)擊啦!');
})
// 點(diǎn)擊按鈕時(shí),才會(huì)打印'我被點(diǎn)擊啦!'
放在函數(shù)定義之前的特殊關(guān)鍵字async創(chuàng)建一個(gè)異步函數(shù):
async function fetchUserNames() {
const resp = await fetch('https://api.github.com/users?per_page=5');
const users = await resp.json();
const names = users.map(({ login }) => login);
console.log(names);
}
fetchUserNames()是異步的,因?yàn)樗那熬Y是async。該函數(shù)await fetch('https://api.github.com/users?per_page=5')從 GitHub 前5個(gè)用戶。然后從響應(yīng)對(duì)象中提取 JSON 數(shù)據(jù):await resp.json()。
async函數(shù)是 Promise 的語法糖。當(dāng)遇到表達(dá)式await <promise>時(shí)(注意,調(diào)用fetch()將返回一個(gè) promise),異步函數(shù)將暫停執(zhí)行直到該promise得以解決。
異步回調(diào)函數(shù)和異步函數(shù)是不同的術(shù)語。
異步回調(diào)函數(shù)由高階函數(shù)以非阻塞方式執(zhí)行。但是異步函數(shù)在等待promise(await <promise>)解析時(shí)暫停其執(zhí)行。
但是,我們可以將異步函數(shù)用作異步回調(diào)!
我們異步函數(shù)fetchUserNames()設(shè)為單擊按鈕時(shí)調(diào)用的異步回調(diào):
const button = document.getElementById('fetchUsersButton');
button.addEventListener('click', fetchUserNames);
回調(diào)是一個(gè)可以作為參數(shù)接受并由另一個(gè)函數(shù)(高階函數(shù))執(zhí)行的函數(shù).
有兩種回調(diào)函數(shù):同步和異步。
同步回調(diào)函數(shù)與使用回調(diào)函數(shù)的高階函數(shù)同時(shí)執(zhí)行,同步回調(diào)是阻塞的。另一方面,異步回調(diào)的執(zhí)行時(shí)間比高階函數(shù)的執(zhí)行時(shí)間晚,異步回調(diào)是非阻塞的。
完~,感謝大家的觀看,我是小智,我去刷碗啦!
作者:Shadeed 譯者:前端小智 來源:dmitripavlutin 原文:https://dmitripavlutin.com/javascript-variables-practices/
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。