現效果:
文同步本人掘金平臺原創翻譯的文章:https://juejin.cn/post/6844903834246971400
你是否遇到過"callbacks"一詞,但是不知道這意味著什么?別著急。你不是一個人。許多JavaScript的新手發現回調也很難理解。
盡管callbacks可能令人疑惑,但是你仍然需要徹底了解它們,因為它們是JavaScript中的一個重要的概念。如果你不知道callbacks,你不可能走得很遠。
這就是今天的文章(要講的)!你將了解callbacks是什么,為什么它們很重要,以及如何使用它們。
備注:你會在這篇文章中看到ES6箭頭函數。如果你不是很熟悉它們,我建議你在往下讀之前復習一下ES6這篇文章(只了解箭頭函數部分就可以了)。
callback是作為稍后要執行的參數傳遞給另一個函數的函數。(開發人員說你在執行函數時“調用”一個函數,這就是被命名為回調函數的原因)。
它們在JavaScript中很常見,你可能自己潛意識的使用了它們而不知道它們被稱為回調函數。
接受函數回調的一個示例是addEventLisnter:
const button=document.querySelector('button')
button.addEventListener('click', function(e) {
// Adds clicked class to button
this.classList.add('clicked')
})
復制代碼
看不出是回調函數嗎?那么,這種寫法怎樣?
const button=document.querySelector('button')
// Function that adds 'clicked' class to the element
function clicked (e) {
this.classList.add('clicked')
}
// Adds click function as a callback to the event listener
button.addEventListener('click', clicked)
復制代碼
在這里,我們告訴JavaScript監聽按鈕上的click事件。如果檢測到點擊,則JavaScript應觸發clicked函數。因此,在這種情況下,clicked是回調函數,而addEventListener是一個接受回調的函數。
現在,你明白什么是回調函數了嘛?:)
我們來看另外一個例子。這一次,假設你希望通過過濾一組數據來獲取小于5的列表。在這里,你將回調函數傳遞給filter函數:
const numbers=[3, 4, 10, 20]
const lesserThanFive=numbers.filter(num=> num < 5)
復制代碼
現在,如果你想通過命名函數執行上面的代碼,則過濾函數將如下所示:
const numbers=[3, 4, 10, 20]
const getLessThanFive=num=> num < 5
// Passing getLessThanFive function into filter
const lesserThanFive=numbers.filter(getLessThanFive)
復制代碼
在這種情況下,getLessThanFive是回調函數。Array.filter是一個接受回調的函數。
現在明白為什么了吧?一旦你知道回調函數是什么,它們就無處不在!
下面的示例向你展示如何編寫回調函數和接受回調的函數:
// Create a function that accepts another function as an argument
const callbackAcceptingFunction=(fn)=> {
// Calls the function with any required arguments
return fn(1, 2, 3)
}
// Callback gets arguments from the above call
const callback=(arg1, arg2, arg3)=> {
return arg1 + arg2 + arg3
}
// Passing a callback into a callback accepting function
const result=callbackAcceptingFunction(callback)
console.log(result) // 6
復制代碼
請注意,當你將回調函數傳遞給另一個函數時,你只傳遞該函數的引用(并沒有執行它,因此沒有括號())
const result=callbackAcceptingFunction(callback)
復制代碼
你只能在callbackAcceptingFunction中喚醒(調用)回調函數。執行此操作時,你可以傳遞回調函數可能需要的任意數量的參數:
const callbackAcceptingFunction=(fn)=> {
// Calls the callback with three args
fn(1, 2, 3)
}
復制代碼
這些由callbackAcceptingFunction傳遞給回調函數的參數,然后再通過回調函數(執行):
// Callback gets arguments from callbackAcceptingFunction
const callback=(arg1, arg2, arg3)=> {
return arg1 + arg2 + arg3
}
復制代碼
這是回調的解剖。現在,你應該知道addEventListener包含一個event參數:)
// Now you know where this event object comes from! :)
button.addEventListener('click', (event)=> {
event.preventDefault()
})
復制代碼
唷!這是callbacks的基本思路!只需要記住其關鍵:將一個函數傳遞給另一個函數,然后,你會想起我上面提到的機制。
旁注:這種傳遞函數的能力是一件很重要的事情。它是如此重要,以至于說JavaScript中的函數是高階函數。高階函數在編程范例中稱為函數編程,是一件很重大的事情。
但這是另一天的話題。現在,我確信你已經開始明白callbacks是什么,以及它們是如何被使用的。但是為什么?你為什么需要callbacks呢?
回調函數以兩種不同的方式使用 -- 在同步函數和異步函數中。
如果你的代碼從上到下,從左到右的方式順序執行,等待上一個代碼執行之后,再執行下一行代碼,則你的代碼是同步的。
讓我們看一個示例,以便更容易理解:
const addOne=(n)=> n + 1
addOne(1) // 2
addOne(2) // 3
addOne(3) // 4
addOne(4) // 5
復制代碼
在上面的例子中,addOne(1)首先執行。一旦它執行完,addOne(2)開始執行。一旦addOne(2)執行完,addOne(3)執行。這個過程一直持續到最后一行代碼執行完畢。
當你希望將部分代碼與其它代碼輕松交換時,回調將用于同步函數。
所以,回到上面的Array.filter示例中,盡管我們將數組過濾為包含小于5的數組,但你可以輕松地重用Array.filter來獲取大于10的數字數組:
const numbers=[3, 4, 10, 20]
const getLessThanFive=num=> num < 5
const getMoreThanTen=num=> num > 10
// Passing getLessThanFive function into filter
const lesserThanFive=numbers.filter(getLessThanFive)
// Passing getMoreThanTen function into filter
const moreThanTen=numbers.filter(getMoreThanTen)
復制代碼
這就是為什么你在同步函數中使用回調函數的原因。現在,讓我們繼續看看為什么我們在異步函數中使用回調。
這里的異步意味著,如果JavaScript需要等待某些事情完成,它將在等待時執行給予它的其余任務。
異步函數的一個示例是setTimeout。它接受一個回調函數以便稍后執行:
// Calls the callback after 1 second
setTimeout(callback, 1000)
復制代碼
如果你給JavaScript另外一個任務需要完成,讓我們看看setTimeout是如何工作的:
const tenSecondsLater=_=> console.log('10 seconds passed!')
setTimeout(tenSecondsLater, 10000)
console.log('Start!')
復制代碼
在上面的代碼中,JavaScript會執行setTimeout。然后,它會等待10秒,之后打印出"10 seconds passed!"的消息。
同時,在等待setTimeout10秒內完成時,JavaScript執行console.log("Start!")。
所以,如果你(在控制臺上)打印上面的代碼,這就是你會看到的:
// What happens:
// > Start! (almost immediately)
// > 10 seconds passed! (after ten seconds)
復制代碼
啊~異步操作聽起來很復雜,不是嗎?但為什么我們在JavaScript中頻繁使用它呢?
要了解為什么異步操作很重要呢?想象一下JavaScript是你家中的機器人助手。這個助手非常愚蠢。它一次只能做一件事。(此行為被稱為單線程)。
假設你告訴你的機器人助手為你訂購一些披薩。但機器人是如此的愚蠢,在打電話給披薩店之后,機器人坐在你家門前,等待披薩送達。在此期間它無法做任何其它事情。
你不能叫它去熨衣服,拖地或在等待(披薩到來)的時候做任何事情。(可能)你需要等20分鐘,直到披薩到來,它才愿意做其他事情...
此行為稱為阻塞。當你等待某些內容完成時,其他操作將被阻止。
const orderPizza=flavour=> {
callPizzaShop(`I want a ${flavour} pizza`)
waits20minsForPizzaToCome() // Nothing else can happen here
bringPizzaToYou()
}
orderPizza('Hawaiian')
// These two only starts after orderPizza is completed
mopFloor()
ironClothes()
復制代碼
而阻止操作是一個無賴。
為什么?
讓我們把愚蠢的機器人助手放到瀏覽器的上下文中。想象一下,當單擊按鈕時,你告訴它更改按鈕的顏色。
這個愚蠢的機器人會做什么?
它專注于按鈕,忽略所有命令,直到按鈕被點擊。同時,用戶無法選擇任何其他內容。看看它都在干嘛了?這就是異步編程在JavaScript中如此重要的原因。
但是,要真正了解異步操作期間發生的事情,我們需要引入另外一個東西 -- 事件循環。
為了設想事件循環,想象一下JavaScript是一個攜帶todo-list的管家。此列表包含你告訴它要做的所有事情。然后,JavaScript將按照你提供的順序逐個遍歷列表。
假設你給JavaScript下面五個命令:
const addOne=(n)=> n + 1
addOne(1) // 2
addOne(2) // 3
addOne(3) // 4
addOne(4) // 5
addOne(5) // 6
復制代碼
這是JavaScript的待辦事項列表中出現的內容。
相關命令在JavaScript待辦事項列表中同步出現。
除了todo-list之外,JavaScript還保留一個waiting-list來跟蹤它需要等待的事情。如果你告訴JavaScript訂購披薩,它會打電話給披薩店并在等候列表名單中添加“等待披薩到達”(的指令)。與此同時,它還會做了其他已經在todo-list上的事情。
所以,想象下你有下面代碼:
const orderPizza (flavor, callback) {
callPizzaShop(`I want a ${flavor} pizza`)
// Note: these three lines is pseudo code, not actual JavaScript
whenPizzaComesBack {
callback()
}
}
const layTheTable=_=> console.log('laying the table')
orderPizza('Hawaiian', layTheTable)
mopFloor()
ironClothes()
復制代碼
JavaScript的初始化todo-list如下:
訂披薩,拖地和熨衣服!
然后,在執行orderPizza時,JavaScript知道它需要等待披薩送達。因此,它會在執行其余任務時,將“等待披薩送達”(的指令)添加到waiting list上。
JavaScript等待披薩到達
當披薩到達時,門鈴會通知JavaScript,當它完成其余雜務時。它會做個**心理記錄(mental note)**去執行layTheTable。
JavaScript知道它需要通過在其 mental note 中添加命令來執行layTheTable
然后,一旦完成其他雜務,JavaScript就會執行回調函數layTheTable。
其他所有內容完成后,JavaScript就會去布置桌面(layTheTable)
我的朋友,這個就被稱為事件循環。你可以使用事件循環中的實際關鍵字替換我們的管家,類比來理解所有的內容:
JavaScript的事件循環
如果你有20分鐘的空余時間,我強烈建議你觀看Philip Roberts 在JSconf中談論的事件循環。它將幫助你理解事件循環的細節。
哦~我們在事件循環繞了一大圈。我們回正題吧。
之前,我們提到如果JavaScript專注于按鈕并忽略所有其他命令,那將是不好的。是吧?
通過異步回調,我們可以提前提供JavaScript指令而無需停止整個操作。
現在,當你要求JavaScript查看點擊按鈕時,它會將“監聽按鈕”(指令)放入waiting list中并繼續進行雜務。當按鈕最終獲得點擊時,JavaScript會激活回調,然后繼續執行。
以下是回調中的一些常見用法,用于告訴JavaScript要做什么...
// Callbacks in event listeners
document.addEventListener(button, highlightTheButton)
document.removeEventListener(button, highlightTheButton)
// Callbacks in jQuery's ajax method
$.ajax('some-url', {
success (data) { /* success callback */ },
error (err) { /* error callback */}
});
// Callbacks in Node
fs.readFile('pathToDirectory', (err, data)=> {
if (err) throw err
console.log(data)
})
// Callbacks in ExpressJS
app.get('/', (req, res)=> res.sendFile(index.html))
復制代碼
這就是它(異步)的回調!
希望你清楚callbacks是什么以及現在如何使用它們。在開始的時候,你不會創建很多回調,所以要專注于學習如何使用可用的回調函數。
現在,在我們結束(本文)之前,讓我們看一下開發人員(使用)回調的第一個問題 -- 回調地獄。
回調地獄是一種多次回調相互嵌套的現象。當你執行依賴于先前異步活動的異步活動時,可能會發生這種情況。這些嵌套的回調使代碼更難閱讀。
根據我的經驗,你只會在Node中看到回調地獄。在使用前端JavaScript時,你幾乎從不會遇到回調地獄。
下面是一個回調地獄的例子:
// Look at three layers of callback in this code!
app.get('/', function (req, res) {
Users.findOne({ _id:req.body.id }, function (err, user) {
if (user) {
user.update({/* params to update */}, function (err, document) {
res.json({user: document})
})
} else {
user.create(req.body, function(err, document) {
res.json({user: document})
})
}
})
})
復制代碼
而現在,你有個挑戰 -- 嘗試一目了然地破譯上面的代碼。很難,不是嗎?難怪開發者在看到嵌套回調時會不寒而栗。
克服回調地獄的一個解決方案是將回調函數分解為更小的部分以減少嵌套代碼的數量:
const updateUser=(req, res)=> {
user.update({/* params to update */}, function () {
if (err) throw err;
return res.json(user)
})
}
const createUser=(req, res, err, user)=> {
user.create(req.body, function(err, user) {
res.json(user)
})
}
app.get('/', function (req, res) {
Users.findOne({ _id:req.body.id }, (err, user)=> {
if (err) throw err
if (user) {
updateUser(req, res)
} else {
createUser(req, res)
}
})
})
復制代碼
更容易閱讀了,是吧?
還有其他解決方案來對抗新版JavaScript中的回調地獄 -- 比如promises和async / await。但是,解釋它們是我們另一天的話題。
今天,你了解到了回調是什么,為什么它們在JavaScript中如此重要以及如何使用它們。你還學會了回調地獄和對抗它的方法。現在,希望callbakcs不再嚇到你了。
你對回調還有任何疑問嗎?如果你有,請隨時在下面發表評論,我會盡快回復你的。【PS:本文譯文,若需作者解答疑問,請移步原作者文章下評論】
感謝閱讀。這篇文章是否幫助到你?如果有,我希望你考慮分享它。你可能會幫助到其他人。非常感謝!
TML 表單用于搜集不同類型的用戶輸入。HTML5 Input,擁有多個新的表單輸入類型,提供了更好的輸入控制和驗證,今天將為大家帶來HTML中的表單及其input輸入類型。
一、HTML表單
1、HTML表單用于收集不同類型的用戶輸入,是一個包含表單元素的區域并且允許用戶在表單中輸入內容,比如文本域(textarea)、下拉列表、單選框(radio=buttons)、復選框(checkboxes)等。
2、表單使用標簽<form>來設置,示例:
運行結果:
二、HTML表單屬性:
1、HTML表單包含表單元素,表單元素是指不同類型的input元素、復選框、單選按鈕、提交按鈕等。
2、action屬性
在上面的示例中出現了action屬性,action屬性定義在提交表單執行的動作,向服務器提交表單的通常做法是使用提交按鈕。
通常,表單會被提交到web服務器上的網頁,上面的例子中,則指定了某個服務器腳本來處理被提交表單。
如果省略 action 屬性,則 action 會被設置為當前頁面。
3、method 屬性
method屬性規定在提交表單時所用的 HTTP 方法(GET 或 POST):
如果表單提交是被動的(比如搜索引擎查詢),并且沒有敏感信息,使用get。
如果表單正在更新數據,或者包含敏感信息(例如密碼),使用post。
4、如果要正確地被提交,每個輸入字段必須設置一個 name 屬性,示例:
<!DOCTYPE html>
<html>
<body>
<form action="/demo/demo_form.asp">
First name:<br>
<input type="text" name="Firstname" value="Mickey">
<br>
Last name:<br>
<input type="text" name="lastname" value="Mouse">
<br><br>
<input type="submit" value="Submit">
</form>
<p>如果您點擊提交,表單數據會被發送到名為 demo_form.asp 的頁面。</p>
<p>first name 不會被提交,因為此 input 元素沒有 name 屬性。</p>
</body>
</html>
運行結果:
5、target 屬性
target 屬性規定提交表單后在何處顯示響應,target 屬性可設置以下值之一:
默認值為 _self,這意味著響應將在當前窗口中打開。
6、Autocomplete 屬性
autocomplete 屬性規定表單是否應打開自動完成功能。
啟用自動完成功能后,瀏覽器會根據用戶之前輸入的值自動填寫值,示例:
運行結果:
7、所有<form>屬性的列表:
三、HTML表單元素:
1、<input>元素是最重要的表單元素,有很多的形態,根據不同的type屬性,例如:
① 文本輸入(text),示例:
<!DOCTYPE html>
<html>
<body>
<form>
First name:<br>
<input type="text" name="firstname">
<br>
Last name:<br>
<input type="text" name="lastname">
</form>
<p>請注意表單本身是不可見的。</p>
<p>同時請注意文本字段的默認寬度是 20 個字符。</p>
</body>
</html>
運行結果:
② 單選按鈕輸入(radio),示例:
<!DOCTYPE html>
<html>
<body>
<form>
<input type="radio" name="sex" value="male" checked>Male
<br>
<input type="radio" name="sex" value="female">Female
</form>
</body>
</html>
運行結果:
③ 提交按鈕(submit),示例:
<!DOCTYPE html>
<html>
<body>
<form action="/demo/demo_form.asp">
First name:<br>
<input type="text" name="firstname" value="Mickey">
<br>
Last name:<br>
<input type="text" name="lastname" value="Mouse">
<br><br>
<input type="submit" value="Submit">
</form>
<p>如果您點擊提交,表單數據會被發送到名為 demo_form.asp 的頁面。</p>
</body>
</html>
運行結果:
2、<select>元素
<select>元素定義下拉列表,示例:
<!DOCTYPE html>
<html>
<body>
<form action="/demo/demo_form.asp">
<select name="cars">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="fiat">Fiat</option>
<option value="audi">Audi</option>
</select>
<br><br>
<input type="submit">
</form>
</body>
</html>
運行結果:
3、<fieldset>元素
<fieldset>元素組合表單中的相關數據
<legend>元素為<fieldset>元素定義標題,示例:
<!DOCTYPE html>
<html>
<body>
<form action="/demo/demo_form.asp">
<fieldset>
<legend>Personal information:</legend>
First name:<br>
<input type="text" name="firstname" value="Mickey">
<br>
Last name:<br>
<input type="text" name="lastname" value="Mouse">
<br><br>
<input type="submit" value="Submit">
</fieldset>
</form>
</body>
</html>
運行結果:
4、<textarea> 元素
<textarea> 元素定義多行輸入字段(文本域)、示例:
<!DOCTYPE html>
<html>
<body>
<form>
<textarea name="message" rows="10" cols="30">
The cat was playing in the garden.
</textarea>
</form>
</body>
</html>
運行結果:
5、HTML5<datalist>元素
<datalist> 元素為 <input> 元素規定預定義選項列表。
用戶會在他們輸入數據時看到預定義選項的下拉列表。
<input> 元素的 list 屬性必須引用 <datalist> 元素的 id 屬性,示例:
<!DOCTYPE html>
<html>
<body>
<form action="/demo/demo_form.asp">
<input list="browsers" name="browser">
<datalist id="browsers">
<option value="Internet Explorer">
<option value="Firefox">
<option value="Chrome">
<option value="Opera">
<option value="Safari">
</datalist>
<input type="submit">
</form>
</body>
</html>
運行結果:
四、HTML表單輸入類型
輸入類型 | 定義 |
text | 定義供文本輸入的單行輸入字段 |
password | 定義密碼字段 |
submit | 定義提交表單數據至表單處理程序的按鈕 |
radio | 定義單選按鈕 |
checkbox | 定義復選框 |
<input>中的type:
類型 | 定義 |
radio | 定義單選按鈕 |
checkbox | 定義復選框 |
button | 定義按鈕 |
number | 用于應該包含數字值的輸入字段 |
date | 用于應該包含日期的輸入字段 |
color | 用于應該包含顏色的輸入字段 |
range | 用于應該包含一定范圍內的值的輸入字段 |
month | 允許用戶選擇月份和年份 |
week | 允許用戶選擇周和年 |
time | 允許用戶選擇時間(無時區) |
datetime | 允許用戶選擇日期和時間(有時區) |
datetime-local | 允許用戶選擇日期和時間(無時區) |
用于應該包含電子郵件地址的輸入字段 | |
search | 用于搜索字段(搜索字段的表現類似常規文本字段) |
tel | 用于應該包含電話號碼的輸入字段 |
url | 用于應該包含 URL 地址的輸入字段 |
輸入限制:
這就是有關HTML表單的大概內容了,希望這篇HTML的表單及其input輸入類型的知識點能對大家有所幫助。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。