现效果:
文同步本人掘金平台原创翻译的文章: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小时内与您取得联系。