JavaScript中的回调函数(callback)
<h2>什么是回调函数</h2><blockquote>
<p>In computer programming, a callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time<br>[引自 维基百科 callback]</p>
</blockquote>
<p>回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。(有点像C语言中的函数指针??)</p>
<p>在JavaScript中函数也是对象的一种,同样对象可以作为参数传递给函数,因此函数也可以作为参数传递给另外一个函数,这个作为参数的函数就是回调函数。</p>
<p>回调函数</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> add(num1, num2, callback){
</span><span style="color: rgba(0, 0, 255, 1)">var</span> sum = num1 +<span style="color: rgba(0, 0, 0, 1)"> num2;
callback(sum);
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> print(num){
console.log(num);
}
add(</span>1, 2, print); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">=>3</span></pre>
</div>
<p>匿名回调函数</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> add(num1, num2, callback){
</span><span style="color: rgba(0, 0, 255, 1)">var</span> sum = num1 +<span style="color: rgba(0, 0, 0, 1)"> num2;
callback(sum);
}
add(</span>1, 2, <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(sum){
console.log(sum); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">=>3</span>
});</pre>
</div>
<p>jQuery中大量的使用了回调函数</p>
<div class="cnblogs_code">
<pre>$("#btn").click(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
alert(</span>"button clicked"<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<h2>我们为什么需要回调?</h2>
<p>有一个非常重要的原因 —— JavaScript 是事件驱动的语言。这意味着,JavaScript 不会因为要等待一个响应而停止当前运行,而是在监听其他事件时继续执行。来看一个基本的例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> first(){
console.log(</span>1<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> second(){
console.log(</span>2<span style="color: rgba(0, 0, 0, 1)">);
}
first();
second();</span></pre>
</div>
<p>正如你所料,<code>first</code> 函数首先被执行,随后 <code>second</code> 被执行 —— 控制台输出下面内容:</p>
<div class="cnblogs_code">
<pre>1
2</pre>
</div>
<div>
<div>但如果函数 <code>first</code> 包含某种不能立即执行的代码会如何呢?例如我们必须发送请求然后等待响应的 API 请求?为了模拟这种状况,我们将使用 <code>setTimeout</code>,它是一个在一段时间之后调用函数的 JavaScript 函数。我们将函数延迟 500 毫秒来模拟一个 API 请求,新代码长这样:</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> first(){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟代码延迟</span>
setTimeout( <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
console.log(</span>1<span style="color: rgba(0, 0, 0, 1)">);
}, </span>500<span style="color: rgba(0, 0, 0, 1)"> );
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> second(){
console.log(</span>2<span style="color: rgba(0, 0, 0, 1)">);
}
first();
second();</span></pre>
</div>
<p>现在理解 <code>setTimeout()</code> 是如何工作的并不重要,重要的是你看到了我们已经把 <code>console.log(1);</code> 移动到了 500 毫秒延迟函数内部。那么现在调用函数会发生什么呢?</p>
<div class="cnblogs_code">
<pre>2
1</pre>
</div>
<p>即使我们首先调用了 <code>first()</code> 函数,我们记录的输出结果却在 <code>second()</code> 函数之后。</p>
<p>这不是 JavaScript 没有按照我们想要的顺序执行函数的问题,而是 <strong>JavaScript 在继续向下执行 <code>second()</code> 之前没有等待 <code>first()</code> 响应</strong>的问题。</p>
<p>所以为什么给你看这个?因为你不能一个接一个地调用函数并希望它们按照正确的顺序执行。<strong>回调正是确保一段代码执行完毕之后再执行另一段代码的方式</strong>。</p>
<h2>回调函数有哪些特点?</h2>
<p><strong>不会立刻执行</strong></p>
<p>回调函数作为参数传递给一个函数的时候,传递的只是函数的定义并不会立即执行。和普通的函数一样,回调函数在函调用函数数中也要通过<code>()</code>运算符调用才会执行。</p>
<p><strong>是个闭包</strong></p>
<p>回调函数是一个闭包,也就是说它能访问到其外层定义的变量。</p>
<p><strong>执行前类型判断</strong></p>
<p>在执行回调函数前最好确认其是一个函数。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> add(num1, num2, callback){
</span><span style="color: rgba(0, 0, 255, 1)">var</span> sum = num1 +<span style="color: rgba(0, 0, 0, 1)"> num2;
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 0, 255, 1)">typeof</span> callback === 'function'<span style="color: rgba(0, 0, 0, 1)">){
callback(sum);
}
}</span></pre>
</div>
<p><strong>this的使用</strong><br>注意在回调函数调用时this的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> obj =<span style="color: rgba(0, 0, 0, 1)"> {
sum: </span>0<span style="color: rgba(0, 0, 0, 1)">,
add: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(num1, num2){
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.sum = num1 +<span style="color: rgba(0, 0, 0, 1)"> num2;
}
};
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> add(num1, num2, callback){
callback(num1, num2);
};
add(</span>1,2<span style="color: rgba(0, 0, 0, 1)">, obj.add);
console.log(obj.sum); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">=>0</span>
console.log(window.sum); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">=>3</span></pre>
</div>
<p>上述代码调用回调函数的时候是在全局环境下,因此this指向的是<code>window</code>,所以<code>sum</code>的值是赋值给<code>windows</code>的。</p>
<p>关于<code>this</code>执行上下文的问题可以通过<code>apply</code>方法解决。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> obj =<span style="color: rgba(0, 0, 0, 1)"> {
sum: </span>0<span style="color: rgba(0, 0, 0, 1)">,
add: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(num1, num2){
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.sum = num1 +<span style="color: rgba(0, 0, 0, 1)"> num2;
}
};
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> add(num1, num2, callbackObj, callback){
callback.apply(callbackObj, [ num1, num2 ]);
};
add(</span>1,2<span style="color: rgba(0, 0, 0, 1)">, obj, obj.add);
console.log(obj.sum); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">=>3</span>
console.log(window.sum); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">=>undefined</span></pre>
</div>
<p><strong>允许传递多个回调函数</strong></p>
<p>一个函数中可以传递多个回调函数,典型的例子如jQuery</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> successCallback() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Do stuff before send</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> successCallback() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Do stuff if success message received</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> completeCallback() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Do stuff upon completion</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> errorCallback() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Do stuff if error received</span>
<span style="color: rgba(0, 0, 0, 1)">}
$.ajax({
url: </span>"http://fiddle.jshell.net/favicon.png"<span style="color: rgba(0, 0, 0, 1)">,
success: successCallback,
complete: completeCallback,
error: errorCallback
});</span></pre>
</div>
<p><strong>回调函数嵌套</strong></p>
<p>一个回调函数中可以嵌入另一个回调函数,对于这种情况出现多层嵌套时,代码会难以阅读和维护,这个时候可以采用命名回调函数的方式调用,或者采用模块化管理函数,也可以用promise模式编程。</p>
<p>从node-mongodb-native,一个适用于Node.js的MongoDB驱动中拿来了一个例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> p_client = <span style="color: rgba(0, 0, 255, 1)">new</span> Db('integration_tests_20', <span style="color: rgba(0, 0, 255, 1)">new</span> Server("127.0.0.1", 27017, {}), {'pk'<span style="color: rgba(0, 0, 0, 1)">:CustomPKFactory});
p_client.open(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(err, p_client) {
p_client.dropDatabase(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(err, done) {
p_client.createCollection(</span>'test_custom_key', <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(err, collection) {
collection.insert({</span>'a':1}, <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(err, docs) {
collection.find({</span>'_id':<span style="color: rgba(0, 0, 255, 1)">new</span> ObjectID("aaaaaaaaaaaa")}, <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(err, cursor) {
cursor.toArray(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(err, items) {
test.assertEquals(</span>1<span style="color: rgba(0, 0, 0, 1)">, items.length);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Let's close the db</span>
<span style="color: rgba(0, 0, 0, 1)"> p_client.close();
});
});
});
});
});
});</span></pre>
</div>
<p> </p>
<p> </p>
<p>参考链接:</p>
<p>1. https://cnodejs.org/topic/564dd2881ba2ef107f854e0b</p>
<p>2. https://juejin.im/post/594b3607128fe100650355c7</p>
<p>3. https://www.jianshu.com/p/84cc8732689c</p>
</div>
</div>
<div id="MySignature" role="contentinfo">
个性签名:时间会解决一切<br><br>
来源:https://www.cnblogs.com/lfri/p/11872294.html
頁:
[1]