Maison >interface Web >js tutoriel >Explication détaillée des fonctions de rappel en JavaScript et comment l'utiliser
En JavaScript, les fonctions sont des objets de première classe, ce qui signifie que les fonctions peuvent être utilisées comme des objets avec une gestion de première classe. Puisque les fonctions sont en fait des objets : elles peuvent être "stockées" dans des variables, passées en tant que paramètres de fonction, créées dans des fonctions et renvoyées par des fonctions.
Les fonctions étant des objets de première classe, nous pouvons utiliser des fonctions de rappel en JavaScript. Dans l'article suivant, nous apprendrons tout sur les fonctions de rappel. Les fonctions de rappel sont probablement la technique de programmation fonctionnelle la plus utilisée en JavaScript. Même si elles ressemblent littéralement à un petit morceau de code JavaScript ou jQuery, elles restent un mystère pour de nombreux développeurs. Après avoir lu cet article, vous comprendrez comment utiliser les fonctions de rappel.
La fonction de rappel est un concept dérivé d'un paradigme de programmation appelé programmation fonctionnelle. En termes simples, la programmation fonctionnelle consiste à utiliser des fonctions comme variables. La programmation fonctionnelle n'était - et n'est toujours - pas largement utilisée - elle était autrefois considérée comme une technique secrète réservée aux programmeurs de niveau master spécialement formés.
Heureusement, la technique de programmation des fonctions a maintenant été entièrement expliquée afin que les gens ordinaires comme moi et vous puissiez l'utiliser facilement. L'une des principales astuces de la programmation fonctionnelle réside dans les fonctions de rappel. Dans le contenu suivant, vous découvrirez qu’implémenter une fonction de rappel est en réalité aussi simple que de transmettre des paramètres à une fonction normale. Cette astuce est si simple que je me demande souvent pourquoi elle est souvent incluse dans les chapitres sur les techniques JavaScript avancées.
Une fonction de rappel, également appelée fonction d'ordre supérieur, est une fonction qui est passé en paramètre à Une fonction d'une autre fonction (ici on appelle l'autre fonction otherFunction
), la fonction de rappel est appelée dans otherFunction
. Une fonction de rappel est essentiellement un modèle de programmation (une solution créée pour un problème courant), c'est pourquoi l'utilisation de fonctions de rappel est également appelée modèle de rappel.
Voici un exemple simple et courant d'utilisation de fonctions de rappel dans jQuery :
//注意到click方法中是一个函数而不是一个变量 //它就是回调函数 $("#btn_1").click(function() { alert("Btn 1 Clicked"); });
Comme vous pouvez le voir dans l'exemple précédent, nous avons passé une fonction en paramètre à la méthode click
. La méthode click
appelle (ou exécute) la fonction que nous lui passons. Il s'agit d'une utilisation typique des fonctions de rappel en JavaScript, et elle est largement utilisée dans jQuery.
Voici un autre exemple de fonction de rappel typique en JavaScript :
var friends = ["Mike", "Stacy", "Andy", "Rick"]; friends.forEach(function (eachName, index){ console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick });
Encore une fois, notez que nous passons une fonction anonyme (une fonction sans nom) en paramètre à forEach
méthode.
Jusqu'à présent, nous avons passé des fonctions anonymes comme arguments à une autre fonction ou méthode. Avant d'examiner des exemples plus pratiques et d'écrire nos propres fonctions de rappel, comprenons comment fonctionnent les fonctions de rappel.
Étant donné que les fonctions sont des objets de première classe en JavaScript, nous traitons les fonctions comme des objets, nous pouvons donc transmettre des fonctions comme des variables, renvoyer des fonctions dans des fonctions et renvoyer des fonctions dans d'autres fonctions utilisées. dans. Lorsque nous passons une fonction de rappel en paramètre à une autre fonction, nous transmettons uniquement la définition de la fonction. Nous n'exécutons pas la fonction sur les paramètres. Nous ne transmettons pas la fonction avec une paire de parenthèses d'exécution ()
comme nous le faisons normalement avec les fonctions d'exécution.
Il est important de noter que la fonction de rappel ne sera pas exécutée immédiatement. Il est "rappelé" (comme son nom l'indique) à un moment précis au sein de la fonction conteneur. Donc même si le premier exemple jQuery ressemble à ceci :
//匿名函数不会再参数中被执行 //这是一个回调函数 $("#btn_1").click(function(){ alert("Btn 1 Clicked"); });
Cette fonction anonyme sera appelée plus tard dans le corps de la fonction. Même s'il a un nom, il est toujours disponible via l'objet arguments
dans la fonction conteneur.
peut passer une fonction de rappel en tant que variable à une autre fonction, la fonction de rappel la contient est exécutée à un certain point dans la fonction comme si la fonction de rappel était définie dans la fonction conteneur. Cela signifie que la fonction de rappel est essentiellement une fermeture.
Comme nous le savons, une fermeture peut entrer dans la portée de la fonction qui la contient, donc la fonction de rappel peut obtenir les variables de la fonction qui la contient, ainsi que les variables dans la portée globale.
Les fonctions de rappel ne sont pas compliquées, mais avant de commencer à créer et à utiliser des fonctions de rappel, nous devons nous familiariser avec eux Plusieurs principes de base pour la mise en œuvre des fonctions de rappel.
Dans l'exemple jQuery précédent et l'exemple forEach, nous avons utilisé celui défini dans la position du paramètre Fonction anonyme comme fonction de rappel. Il s'agit d'un tour de magie courant dans l'utilisation des fonctions de rappel. Un autre modèle courant consiste à définir une fonction nommée et à transmettre le nom de la fonction en tant que variable à la fonction. Par exemple, l'exemple suivant :
//全局变量 var allUserData = []; //普通的logStuff函数,将内容打印到控制台 function logStuff (userData){ if ( typeof userData === "string"){ console.log(userData); } else if ( typeof userData === "object"){ for(var item in userData){ console.log(item + ": " + userData[item]); } } } //一个接收两个参数的函数,后面一个是回调函数 function getInput (options, callback){ allUserData.push(options); callback(options); } //当我们调用getInput函数时,我们将logStuff作为一个参数传递给它 //因此logStuff将会在getInput函数内被回调(或者执行) getInput({name:"Rich",speciality:"Javascript"}, logStuff); //name:Rich //speciality:Javascript
既然回调函数在执行时仅仅是一个普通函数,我们就能给它传递参数。我们能够传递任何包含它的函数的属性(或者全局属性)作为回调函数的参数。在前面的例子中,我们将options作为一个参数传递给了回调函数。现在我们传递一个全局变量和一个本地变量:
//全局变量 var generalLastName = "Cliton"; function getInput (options, callback){ allUserData.push (options); //将全局变量generalLastName传递给回调函数 callback(generalLastName,options); }
在调用之前检查作为参数被传递的回调函数确实是一个函数,这样的做法是明智的。同时,这也是一个实现条件回调函数的最佳时间。
我们来重构上面例子中的getInput
函数来确保检查是恰当的。
function getInput(options, callback){ allUserData.push(options); //确保callback是一个函数 if(typeof callback === "function"){ //调用它,既然我们已经确定了它是可调用的 callback(options); } }
如果没有适当的检查,如果getInput
的参数中没有一个回调函数或者传递的回调函数事实上并不是一个函数,我们的代码将会导致运行错误。
当回调函数是一个this
对象的方法时,我们必须改变执行回调函数的方法来保证this
对象的上下文。否则如果回调函数被传递给一个全局函数,this
对象要么指向全局window
对象(在浏览器中)。要么指向包含方法的对象。
我们在下面的代码中说明:
//定义一个拥有一些属性和一个方法的对象 //我们接着将会把方法作为回调函数传递给另一个函数 var clientData = { id: 094545, fullName "Not Set", //setUsrName是一个在clientData对象中的方法 setUserName: fucntion (firstName, lastName){ //这指向了对象中的fullName属性 this.fullName = firstName + " " + lastName; } } function getUserInput(firstName, lastName, callback){ //在这做些什么来确认firstName/lastName //现在存储names callback(firstName, lastName); }
在下面你的代码例子中,当clientData.setUsername
被执行时,this.fullName
并没有设置clientData
对象中的fullName
属性。相反,它将设置window
对象中的fullName
属性,因为getUserInput
是一个全局函数。这是因为全局函数中的this
对象指向window
对象。
getUserInput("Barack","Obama",clientData.setUserName); console.log(clientData,fullName); //Not Set //fullName属性将在window对象中被初始化 console.log(window.fullName); //Barack Obama
我们可以使用Call
或者Apply
函数来修复上面你的问题。到目前为止,我们知道了每个JavaScript中的函数都有两个方法:Call
和 Apply
。这些方法被用来设置函数内部的this对象以及给此函数传递变量。
call
接收的第一个参数为被用来在函数内部当做this
的对象,传递给函数的参数被挨个传递(当然使用逗号分开)。Apply
函数的第一个参数也是在函数内部作为this
的对象,然而最后一个参数确是传递给函数的值的数组。
听起来很复杂,那么我们来看看使用Apply
和Call
有多么的简单。为了修复前面例子的问题,我将在下面你的例子中使用Apply
函数:
//注意到我们增加了新的参数作为回调对象,叫做“callbackObj” function getUserInput(firstName, lastName, callback. callbackObj){ //在这里做些什么来确认名字 callback.apply(callbackObj, [firstName, lastName]); }
使用Apply
函数正确设置了this
对象,我们现在正确的执行了callback
并在clientData
对象中正确设置了fullName
属性:
//我们将clientData.setUserName方法和clientData对象作为参数,clientData对象会被Apply方法使用来设置this对象 getUserName("Barack", "Obama", clientData.setUserName, clientData); //clientData中的fullName属性被正确的设置 console.log(clientUser.fullName); //Barack Obama
我们也可以使用Call
函数,但是在这个例子中我们使用Apply
函数。
我们可以将不止一个的回调函数作为参数传递给一个函数,就像我们能够传递不止一个变量一样。这里有一个关于jQuery中AJAX的例子:
function successCallback(){ //在发送之前做点什么 } function successCallback(){ //在信息被成功接收之后做点什么 } function completeCallback(){ //在完成之后做点什么 } function errorCallback(){ //当错误发生时做点什么 } $.ajax({ url:"http://fiddle.jshell.net/favicon.png", success:successCallback, complete:completeCallback, error:errorCallback });
在执行异步代码时,无论以什么顺序简单的执行代码,经常情况会变成许多层级的回调函数堆积以致代码变成下面的情形。这些杂乱无章的代码叫做回调地狱因为回调太多而使看懂代码变得非常困难。我从node-mongodb-native,一个适用于Node.js的MongoDB驱动中拿来了一个例子。这段位于下方的代码将会充分说明回调地狱:
var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory}); p_client.open(function(err, p_client) { p_client.dropDatabase(function(err, done) { p_client.createCollection('test_custom_key', function(err, collection) { collection.insert({'a':1}, function(err, docs) { collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { cursor.toArray(function(err, items) { test.assertEquals(1, items.length); // Let's close the db p_client.close(); }); }); }); }); }); });
你应该不想在你的代码中遇到这样的问题,当你当你遇到了
你将会时不时的遇到这种情况
这里有关于这个问题的两种解决方案。
给你的函数命名并传递它们的名字作为回调函数,而不是主函数的参数中定义匿名函数。
模块化L将你的代码分隔到模块中,这样你就可以到处一块代码来完成特定的工作。然后你可以在你的巨型应用中导入模块。
既然你已经完全理解了关于JavaScript中回调函数的一切(我认为你已经理解了,如果没有那么快速的重读以便),你看到了使用回调函数是如此的简单而强大,你应该查看你的代码看看有没有能使用回调函数的地方。回调函数将在以下几个方面帮助你:
避免重复代码(DRY-不要重复你自己)
在你拥有更多多功能函数的地方实现更好的抽象(依然能保持所有功能)
让代码具有更好的可维护性
使代码更容易阅读
编写更多特定功能的函数
创建你的回调函数非常简单。在下面的例子中,我将创建一个函数完成以下工作:读取用户信息,用数据创建一首通用的诗,并且欢迎用户。这本来是个非常复杂的函数因为它包含很多if/else
语句并且,它将在调用那些用户数据需要的功能方面有诸多限制和不兼容性。
相反,我用回调函数实现了添加功能,这样一来获取用户信息的主函数便可以通过简单的将用户全名和性别作为参数传递给回调函数并执行来完成任何任务。
简单来讲,getUserInput
函数是多功能的:它能执行具有无种功能的回调函数。
//首先,创建通用诗的生成函数;它将作为下面的getUserInput函数的回调函数 function genericPoemMaker(name, gender) { console.log(name + " is finer than fine wine."); console.log("Altruistic and noble for the modern time."); console.log("Always admirably adorned with the latest style."); console.log("A " + gender + " of unfortunate tragedies who still manages a perpetual smile"); } //callback,参数的最后一项,将会是我们在上面定义的genericPoemMaker函数 function getUserInput(firstName, lastName, gender, callback) { var fullName = firstName + " " + lastName; // Make sure the callback is a function if (typeof callback === "function") { // Execute the callback function and pass the parameters to it callback(fullName, gender); } }
调用getUserInput
函数并将genericPoemMaker
函数作为回调函数:
getUserInput("Michael", "Fassbender", "Man", genericPoemMaker); // 输出 /* Michael Fassbender is finer than fine wine. Altruistic and noble for the modern time. Always admirably adorned with the latest style. A Man of unfortunate tragedies who still manages a perpetual smile. */
因为getUserInput
函数仅仅只负责提取数据,我们可以把任意回调函数传递给它。例如,我们可以传递一个greetUser
函数:
unction greetUser(customerName, sex) { var salutation = sex && sex === "Man" ? "Mr." : "Ms."; console.log("Hello, " + salutation + " " + customerName); } // 将greetUser作为一个回调函数 getUserInput("Bill", "Gates", "Man", greetUser); // 这里是输出 Hello, Mr. Bill Gates
我们调用了完全相同的getUserInput
函数,但是这次完成了一个完全不同的任务。
正如你所见,回调函数很神奇。即使前面的例子相对简单,想象一下能节省多少工作量,你的代码将会变得更加的抽象,这一切只需要你开始使用毁掉函数。大胆的去使用吧。
在JavaScript编程中回调函数经常以几种方式被使用,尤其是在现代Web应用开发以及库和框架中:
异步调用(例如读取文件,进行HTTP请求,等等)
时间监听器/处理器
setTimeout
和setInterval
方法
一般情况:精简代码
JavaScript回调函数非常美妙且功能强大,它们为你的Web应用和代码提供了诸多好处。你应该在有需求时使用它;或者为了代码的抽象性,可维护性以及可读性而使用回调函数来重构你的代码。
推荐教程:《JavaScript视频教程》
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!