当谈到javascript回调时我很困惑...尤其是在尝试同时学习节点时。 你知道,我理解将一个函数作为参数传递给另一个函数的概念,但让我感到困惑的主要是预制模块,它们接受了#34;回调函数。
例如,一个简单的请求如下:
var request = require('request');
var url = 'http://www.google.com';
request.get(url, function(error, response, body) {
if(error){
console.log('This request resulted in an error', error);
}
console.log(body);
});
此get方法接受url和回调。 谁定义了回调被接受?具体方法的开发者? 此外,我们在函数内传递的3个参数(错误,响应和正文)...请求模块如何知道在返回时使用数据填充这些变量?
答案 0 :(得分:1)
想象一下,get
或多或少地实现了
function get(url, callback) {
var response = they somehow retrieve the response;
var body = they somehow parse the body;
var error = ...;
// they call you back using your callback
callback( error, response, body );
}
如何计算这些值是无关紧要的(这是他们的实际工作)但是他们只是回调你,假设你的函数实际上接受了给定顺序的三个参数(如果不是你只是得到运行时错误)。
从这个意义上说,他们也有点接受"你的回调是......好吧,回调它:)不用担心参数的数量,因为Javascript非常宽容" - 可以使用任意数量的参数调用方法,包括比函数期望的更少或更多的参数。
以此为例:
// they provide 3 arguments
function get( callback ) {
callback( 0, 1, 2 );
}
// but you seem to expect none
get( function() {
});
get
假设回调接受三个参数,而我传递的方法据说不接受 less 参数。这是有效的,虽然三个参数0
,1
和2
没有绑定到任何正式的回调参数(你不能直接引用它们),它们在那里arguments
对象,类似于数组的结构:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
再举一个例子
function get( callback ) {
callback( 0, 1, 2 );
}
get( function(a,b,c,d,e) {
});
这一次我通过的回调似乎比调用者提供的更多参数更多,但这里没问题。参数绑定到连续的调用参数,a
将获得0,b
将获得1,c
将获得2而d
和e
将获得{{ 1}}在回调体内。
这两个例子都表明,调用者并不关心你的回调是如何定义的,无论它的参数数量是否符合预期。如果出现任何不匹配,可能发生的最坏情况是运行时错误:
undefined
答案 1 :(得分:1)
此get方法接受url和回调。谁定义了回调被接受?具体方法的开发者?
是。 request.get()
的作者专门定义它以期望并使用"回调"功能
您也可以定义自己的函数来接受回调,当它们依赖于其他异步函数时尤其有用 - 例如setTimeout()
的简单示例。
function delay(value, callback) {
setTimeout(function () {
// this statement defines the arguments given to the callback
// it specifies that only one is given, which is the `value`
callback(value);
}, 1000);
}
delay('foo', function (value) { // named to match the argument `delay` provides
console.log(value);
});
此外,我们在函数中传递的3个参数(错误,响应和正文)...请求模块如何知道在返回时使用数据填充这些变量?
给出的参数及其顺序是request
定义的一部分,与上面的delay()
相同,定义(value)
将是其callback
的参数1}}。
提供回调时,参数仅提供您自己使用的参数名称。它们对主函数(request.get()
,delay()
等)的行为没有影响。
即使没有命名任何参数,request.get()
仍然以相同的顺序将相同的3个参数传递给回调:
request.get(url, function () { // no named parameters
var error = arguments[0]; // still the 1st argument at `0` index
var body = arguments[2]; // still the 3rd argument at `2` index
if(error){
console.log('This request resulted in an error', error);
}
console.log(body);
});
答案 2 :(得分:1)
函数/方法的设计者决定它将接受和处理的参数。因此,如果函数接受回调作为参数,那么函数的设计和实现决定了它并在适当的时候调用回调。
它也是函数/方法的实现者,它决定在调用时将哪些参数传递给回调。令许多人感到困惑的是,函数的调用者定义了将由main函数/方法调用的回调函数本身。但是必须声明回调函数的参数以匹配main函数/方法将传递给回调的函数。定义回调时给定参数的标签仅仅是编程方便的标签(作为通过名称引用参数的一种方式)。如何在回调函数中定义回调的参数与实际传递给回调的内容无关 - 这全部由调用回调的函数/方法决定。因此,当您使用request.get()
时,使用它的合同是它接受一个应该被声明为具有参数(error, response, body)
的回调,因为这是它将被调用的内容。现在,如果你不打算使用它们,可以在最后留下参数(它们仍然会存在,但你只是没有方便的名称来引用它们),但是你不能在您打算使用的任何内容之前不要使用参数,因为顺序很重要。
谁定义接受回调?特定的开发人员 方法
是的,函数/方法的开发人员决定是否接受回调。
此外,我们在函数中传递的3个参数(错误, 响应和正文)...请求模块如何知道填充 这些变量带有返回数据吗?
request.get()
方法被编程为将三个参数(error, response, body)
传递给它的回调,这就是它将传递的内容。无论回调本身如何实际声明,request.get()
将按顺序传递这三个参数。因此,让许多人感到困惑的是函数/方法的调用者自己创建了回调函数,并为它定义了参数。好吧,不是真的。回调的创建者必须创建一个回调,匹配函数/方法期望的契约。如果它与合同不匹配,则回调可能无法正常运行,因为它会对传递的内容感到困惑。这里的合同真的是回调的第一个参数是error
值,如果该值不是真的,那么接下来的两个参数将是response
和body
。您的回调必须与该联系人匹配才能正确使用这些参数。因为Javascript没有严格类型,如果你错误地声明你的回调,它将不会是一个运行时错误,但你对这些参数的访问可能是错误的 - 导致事情工作不正确。
例如,如果您将回调声明为:
function myCallback(response, body, err) { .... }
然后,你的回调看到的response
实际上是error
值,因为request.get()
将错误值放在第一个参数中,无论回调本身是如何声明的。 / p>
另一种看待这种情况的方法是定义一个没有定义参数的回调,然后使用arguments
对象完全访问它们。
function myCallback() {
console.log(arguments[0]); // err value
console.log(arguments[1]); // response value
console.log(arguments[2]); // body value
}
回调中的返回值也是如此。如果您使用Array.prototye.map()
,它需要一个传递三个参数的回调并返回一个新的数组元素。即使您正在实现回调,也必须定义与该合同兼容的回调。