侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

js回调函数总结

2024-05-13 星期一 / 0 评论 / 0 点赞 / 95 阅读 / 14684 字

###1.为什么要使用js回调函数我们经常要去做一件事,然后我们可能做完了之后希望再去做别的事,具体是什么式,我可能不确定,我们希望具体情景,具体定制。 举个例子:我希望实现一个通用函数,它可以在我

###1.为什么要使用js回调函数

我们经常要去做一件事,然后我们可能做完了之后希望再去做别的事,具体是什么式,我可能不确定,我们希望具体情景,具体定制。  

举个例子:
我希望实现一个通用函数,它可以在我洗完衣服之后,我要去做点别的(比如有可能打篮球,有可能打乒乓球,有可能打羽毛球,)。但是具体做什么事,这就要根据我当时的具体情况而定,但是要支持所有想做的事,这个如果用一个函数肯定是解决不了的问题,会是如下解决的。
function do(){

   1.洗衣服     2.打篮球   

}

function do1(){

   1.洗衣服   2.打乒乓球   

}

function do2(){

   1.洗衣服   2.打羽毛球  

}

这儿就忘了变量的灵活性。这其实就可以使用回调函数来解决,就是我的一个函数A就可以做洗衣服,但是我可以接受一个打篮球的函数做参数,你传过来什么,我就做什么。伪代码如下
//1.定义通用函数
fucntion do(function){

 1.洗衣服  2 //做具体的事  function();   

}
//2.使用通用函数

a.想洗完衣服之后,打篮球,调用通用函数
do(funciton(){

  打篮球      

});

b.想洗完衣服之后,打羽毛球,调用通用函数
do(funciton(){

  打乒乓球      

});

c.想洗完衣服之后,打乒乓球,调用通用函数
do(funciton(){

  打乒乓球      

});

这样就可以实现,我只写一个通用函数,但是洗完衣服之后具体做什么,由调用者自己去决定的模式。

就是一种回调模式。回调函数的本质是一种模式(一种解决常见问题的模式),因此回调函数也被称为回调模式。

百度百科里:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

javascript里面的回调函数的定义:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。

###2.为什么javascipt里面可以使用回调函数。

在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String、Array、Number、Object类的对象一样用于内置对象的管理。因为function实际上是一种对象,它可以“存储在变量中,通过参数传递给(别一个)函数(function),在函数内部创建,从函数中返回结果值”。因为function是内置对象,我们可以将它作为参数传递给另一个函数,延迟到函数中执行,甚至执行后将它返回。这是在JavaScript中使用回调函数的精髓回调函数也许是JavaScript中使用最广泛的功能性编程技术,也许仅仅一小段JavaScript或jQuery的代码都会给开发者留下一种神秘感,阅读这篇文章后,也许会帮你消除这种神秘感。

###3.自己编写自定义回调函数

这里输入代码<!DOCTYPE html><html><head>	<meta charset="UTF-8">	<title>回调 </title>	<script type="text/javascript" src="jquery-1.10.2.js"></script>	<script type="text/javascript">			function doSomething(a,func){			alert(a);			if(typeof func == "function") //这儿很重要,因为这儿表示这个参数是函数类型,必须检测,因为javascript是弱类型语言。			{				func();  //func.call(this);或者func.apply(this);			}			}		function PlayBasketBall(){			doSomething("洗衣服",function(){				alert("打篮球")			});		}		function PlayTableTennis(){			doSomething("洗衣服",function(){				alert("打乒乓球")			});		}		function PlayBadminton(){			doSomething("洗衣服",function(){				alert("打羽毛球")			});		}	</script></head><body>	<button onclick="PlayBasketBall()">洗完衣服打篮球</button>	<button onclick="PlayTableTennis()">洗完衣服打乒乓球</button>	<button onclick="PlayBadminton()">洗完衣服打羽毛球</button></body><html>

其实在jquery里面经常的使用回调函数,例如:

//The anonymous function is not being executed there in the parameter. //The item is a callback function$("#btn_1").click(function() {  alert("Btn 1 Clicked");});

匿名函数将延迟在click函数的函数体内被调用,即使没有名称,也可以被包含函数通过 arguments对象访问。

回调函数是闭包的当作为参数传递一个回调函数给另一个函数时,回调函数将在包含函数函数体内的某个位置被执行,就像回调函数在包含函数的函数体内定义一样。这意味着回调函数是闭包的,众所周知,闭包函数可以访问包含函数的作用域,所以,回调函数可以访问包含函数的变量,甚至是全局变量。

4.javacript里面编写回调函数相关注意事项

简单地说,自己实现回调函数的时候需要遵循几条原则。

4.1使用命名函数或匿名函数作为回调

我们上面的示例中使用的匿名函数,这是写回调函数最通用的方式之一,其实可以使用命名函数的。

4.2传递参数给回调函数


// global variablevar allUserData = [];// generic logStuff function that prints to consolefunction logStuff (userData) {    if ( typeof userData === "string")    {        console.log(userData);    }    else if ( typeof userData === "object")    {        for (var item in userData) {            console.log(item + ": " + userData[item]);        }    }}// A function that takes two parameters, the last one a callback functionfunction getInput (options, callback) {    allUserData.push (options);    callback (options);}// When we call the getInput function, we pass logStuff as a parameter.// So logStuff will be the function that will called back (or executed) inside the getInput functiongetInput ({name:"Rich", speciality:"JavaScript"}, logStuff);//  name: Rich// speciality: JavaScript

因为回调函数在执行的时候就和一般函数一样,我们可以传递参数给它。可以将包含函数的任何属性(或全局的属性)作为参数传递回调函数。在上一个例子中,我们将包含函数的options作为参数传递给回调函数。下面的例子让我们传递一个全局变量或本地变量给回调函数:

 //Global variable var generalLastName = "Clinton"; function getInput (options, callback) {    allUserData.push (options); // Pass the global variable generalLastName to the callback function   callback (generalLastName, options); }

4.3在执行之前确保回调是一个函数----- typeof检测变量类型

在调用之前,确保通过参数传递进来的回调是一个需要的函数通常是明智的。此外,让回调函数是可选的也是一个好的实践。

让我们重构一下上面例子中的getInput函数,确保回调函数做了适当的检查。

function getInput(options, callback) {    allUserData.push(options);    // Make sure the callback is a function    if (typeof callback === "function") {    // Call it, since we have confirmed it is callable        callback(options);    }}

如果getInput函数没有做适当的检查(检查callback是否是函数,或是否通过参数传递进来了),我们的代码将会导致运行时错误。

4.4使用含有this对象的回调函数的问题

当回调函数是一个含有this对象的方法时,我们必须修改执行回调函数的方法以保护this对象的内容。否则this对象将会指向全局的window对象(如果回调函数传递给了全局函数),或指向包含函数。让我们看看下面的代码:

// Define an object with some properties and a method// We will later pass the method as a callback function to another functionvar clientData = {    id: 094545,    fullName: "Not Set",    // setUserName is a method on the clientData object    setUserName: function (firstName, lastName)  {        // this refers to the fullName property in this object      this.fullName = firstName + " " + lastName;    }}function getUserInput(firstName, lastName, callback)  {    // Do other stuff to validate firstName/lastName here    // Now save the 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// The fullName property was initialized on the window objectconsole.log (window.fullName); // Barack Obama

使用Call或Apply函数保护this对象

我们可以通过使用 Call 或 Apply函数来解决前面示例中的问题。到目前为止,我们知道JavaScript中的每一个函数都有两个方法:Call和Apply。这些方法可以被用来在函数内部设置this对象的内容,并内容传递给函数参数指向的对象。

Call takes the value to be used as the this object inside the function as the first parameter, and the remaining arguments to be passed to the function are passed individually (separated by commas of course). The Apply function’s first parameter is also the value to be used as the thisobject inside the function, while the last parameter is an array of values (or the arguments object) to pass to the function.  (该段翻译起来太拗口了,放原文自己体会)

这听起来很复杂,但让我们看看Apply和Call的使用是多么容易。为解决前面例子中出现的问题,我们使用Apply函数如下:

//Note that we have added an extra parameter for the callback object, called "callbackObj"function getUserInput(firstName, lastName, callback, callbackObj)  {    // Do other stuff to validate name here    // The use of the Apply function below will set the this object to be callbackObj    callback.apply (callbackObj, [firstName, lastName]);}

通过Apply函数正确地设置this对象,现在我们可以正确地执行回调函数并它正确地设置clientData对象中的fullName属性。

// We pass the clientData.setUserName method and the clientData object as parameters. The clientData object will be used by the Apply function to set the this object
getUserInput ("Barack", "Obama", clientData.setUserName, clientData);// the fullName property on the clientData was correctly setconsole.log (clientData.fullName); // Barack Obama

我们也可以使用Call 函数,但在本例中我们使用的Apply 函数。

4.5多重回调函数也是允许的

我们可以传递多个回调函数给另一个函数,就像传递多个变量一样。这是使用jQuery的AJAX函数的典型例子:

function successCallback() {    // Do stuff before send}function successCallback() {    // Do stuff if success message received}function completeCallback() {    // Do stuff upon completion}function errorCallback() {    // Do stuff if error received}$.ajax({    url:"http://fiddle.jshell.net/favicon.png",    success:successCallback,    complete:completeCallback,    error:errorCallback});

4.6“回调地狱”的问题和解决方案

异步代码执行是一种简单的以任意顺序执行的方式,有时是很常见的有很多层级的回调函数,你看起来像下面这样的代码。下面这种凌乱的代码称作“回调地狱”,因为它是一种包含非常多的回调的麻烦的代码。我是在node-mongodb-native里看到这个例子的,MongoDB驱动Node.js.示例代码就像这样:

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();                    });                });            });        });    });});

你不太可能在自己的代码里碰到这个的问题,但如果你碰到了(或以后偶然碰到了),那么有以下两种方式解决这个问题。

命名并定义你的函数,然后传递函数名作为回调,而不是在主函数的参数列表里定义一个匿名函数。模块化:把你的代码划分成一个个模块,这样你可以空出一部分代码块做特殊的工作。然后你可以将这个模型引入到你的大型应用程序中。

5.总结

我们应该看看自己的代码是否有机会使用回调函数,有以下需求时你可以考虑使用回调:

避免重复代码 (DRY—Do Not Repeat Yourself)
在你需要更多的通用功能的地方更好地实现抽象(可处理各种类型的函数)。
增强代码的可维护性
增强代码的可读性
有更多定制的功能

广告 广告

评论区