「コールバック」を偶然目にしたものの、それが何を意味するのか知らなかったということはありませんか?心配しないで。あなたは一人じゃない。 JavaScript の初心者の多くは、コールバックを理解するのが困難です。
コールバックは混乱を招く可能性がありますが、JavaScript では重要な概念であるため、コールバックを完全に理解することを学ぶ必要があります。プルバックを知らなければ、遠くへは進めません。
今日の記事で説明する必要があるのはこれです。コールバックとは何か、コールバックが重要である理由、およびその使用方法を学びます。
この記事では、ES6 のアロー関数について説明します。まだよく知らない場合は、まず ES6 投稿 をチェックすることをお勧めします。 (アロー関数のセクションを読んでください)。
コールバックはコールバックと呼ばれます)。
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)
click イベントをボタンにバインドします。クリック時間が検出されると、JavaScript は
clicked 関数を実行します。したがって、この例では、
addEventListener 関数がコールバック関数を受け取ると、
clicked がコールバックになります。
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)`
You Thisコールバックは、
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() })
ふーん!これがコールバックの基本的な意味です。キーワードを覚えておいてください。ある関数を別の関数に渡すと、上記のメカニズムを思い出すことができます。
関数を渡すこの機能は重要です。これは非常に大きいため、JavaScript の関数は高階関数になります。高階関数は関数型プログラミング パラダイムにおいて非常に重要です。
しかし、私たちは今この話題について話し合うつもりはありません。さて、コールバックとその使用方法についてはすでにご存知かと思います。しかし、なぜコールバックを使用する必要があるのでしょうか?
コードの実行が上から下、do から右に順番に実行される場合、コードは実行されるまで待機します。コードの次の行が実行される前にコードの実行が完了すると、コードは同期になります。
理解を容易にするために例を見てみましょう: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 需要等待某个东西完成,在等待的过程中会执行其余的任务。 一个异步函数例子就是 如果你给JavaScript 另一个任务去完成时我们看看 在上面的代码里,JavaScript 去执行 同时,在等到10秒去执行 因此,如果你记录上面的代码,你会看到这一点。 啊。异步操作听起来很复杂,不是么?但是我们为什么在 JavaScript 里到处使用呢? 要理解为什么异步操作很重要,想象一下 JavaScript 是你家里的一个机器人助手。这个助手很蠢。一次只能做一件事情。(这个行为称之为单线程)。 假设你告诉机器人助手帮你订点披萨。但是机器人助手如此蠢,在给披萨店打完电话后,机器人助手坐在你家门前,慢慢的等待披萨送来。在这个过程中不能做任何其他的事情。 等待的过程中,你不能让它去熨烫衣服,拖地板以及其他任何事情。你需要等20分钟,直到披萨送来,才愿意做其他的事情。 这个行为称之为阻塞。在等待一个任务执行完全之前,其他的操作被阻止了。 现在,阻塞操作是非常令人失望的。 为什么? 我们把愚蠢的机器人助手放在浏览器的运行环境里。想象一下,当按钮被点击时需要改变按钮的颜色。 那这个愚蠢的机器人会怎么做呢? 它会凝视着这个按钮,在按钮被点击之前,忽略掉其他任何的命令。同时,用户不能选择其他任何东西。看看现在这样的情况?这就是异步编程在 JavaScript 为什么如此重要。 但是真正理解在异步操作过程中发生了什么,我们需要理解另外一个东西-事件循环。 事件循环 想象事件循环,可以想象 JavaScript 是一个 todo-list 的管家。这个列表包含了所有你告诉它的事情。JavaScript 会按照你给的顺序,一步步的遍历这个列表。 假设你给JavaScript 的5个命令如下: 这将会出现在 JavaScript 的todo 列表里。 命令在 JavaScript 的 todo 列表里同步显示。 除了 todo 列表,JavaScript 还保存了一个 waiting 列表,这个列表可以跟踪需要等待的东西。如果你告诉 JavaScript 需要定披萨,它会给披萨店打电话,并把"等待披萨送来"加到等到列表里。同时,它会做 todo 列表已经有的事情。 所以,想象一下有这样的代码。 JavaScript 的初始列表将会是: 定披萨,拖地和熨烫衣服! 这是,当执行到 JavaScript 等待披萨到达。 当披萨送到时,按门铃会通知 JavaScript并做一个标记,当处理完其他杂事时,会去执行 JavaScript 知道通过标记里的命令需要去执行 然后,一旦处理完了其他的杂务,JavaScript 就会执行回调函数 当其他一切都完成时, JavaScript 会将其放置。 这就是我的朋友,事件循环。你可以用事件循环中的实际关键字来替代我们的巴特勒类比来理解所有的事情。 Todo-list-> Call stack Waiting-list-> Web apis Mental note-> Event queue JavaScript 事件循环 如果你有20分钟空闲时间的话,我强烈推荐你看Philip Roberts在 JSConf 上关于事件循环的演讲。它会帮助你了解事件循环里的细节。 哦。我们在事件循环上转了个大圈。现在我们回头来看。 之前,我们提到如果 JavaScript 专注地盯着一个按钮并忽略其他所有的命令,这是非常糟糕的。是吧? 通过异步回调,我们可以提前给 JavaScript 指令而不需要停止整个操作。 现在,当你让 JavaScript 监听一个按钮的点击事件时,它将"监听按钮"放在等待列表里,然后继续做家务。当按钮最终获取到点击事件时,JavaScript 会激活回调,然后继续运行 下面是一些常见的回调函数,告诉 JavaScript 应该怎么做: 当事件被触发(比如: Ajax 执行之后(比如: 文件读写之后(比如: 这就是回调! 希望,你现在已经弄清楚了回调是什么并且怎么去使用。在最开始的时候,你没必要创建很多的回调,更多的去专注于学习如何使用可用的回调函数。 现在,在结束之前,我们来看看回调的第一个问题 - 回调地狱 回调地狱是在多个回调嵌套出现时的一个现象。它发生在一个异步回调执行依赖上一个异步回调执行的时候。这些嵌套的回调会导致代码非常难以理解。 在我的经验里,你只会在 Node.js 里看到回调地狱。当你的 JavaScript 在前台运行时一般都不会遇到回调地狱。 这里有一个回调地狱的例子: 现在,对你来说,解读上面的代码是一个挑战。相当的难,不是么?难怪在看到嵌套回调时,开发人员会不寒而栗。 解决回调的一个解决方案是将回调函数分解成更小的部分,以减少嵌套代码的数量setTimeout
。它会一段时间后执行回调函数。// Calls the callback after 1 second
setTimeout(callback, 1000)
setTimeout
是怎么工作的:const tenSecondsLater = _ = > console.log('10 seconds passed!')
setTimeout(tenSecondsLater, 10000)
console.log('Start!')
setTimeout
。这时,会等待10
秒且打印日志“10 seconds passed!”。setTimeout
时,JavaScript 会执行console.log("Start!")
。// What happens:
// > Start! (almost immediately)
// > 10 seconds passed! (after ten seconds)
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()
const addOne = (n) => n + 1
addOne(1) // 2
addOne(2) // 3
addOne(3) // 4
addOne(4) // 5
addOne(5) // 6
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()
orderPizza
,JavaScript 知道需要等待披萨送来。因此,在把"等待披萨送来"加到等待列表中的同时会处理剩下的工作。layTheTable
。layTheTable
。layTheTable
。为什么回调如此重要?
addEventListener
)jQuery.ajax
)fs.readFile
)// 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))
回调地狱
// 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
。但是,会在另一个话题中解析它们。
今天,我们学习了什么是回调,为什么会如此重要以及如何去使用它们。同时学习到了什么是回调地狱,以及如何避免。希望,回调不会让你感到害怕。
关于回调你还有其他的问题么?如果你有的话,请在下面留言,我会尽快回复你的。
相关免费学习推荐:js视频教程
以上がJS のコールバックを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。