问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

关于JavaScript中回调函数疑问?

发布网友 发布时间:2022-04-28 17:32

我来回答

4个回答

懂视网 时间:2022-04-22 14:03

平常的前端开发工作中,编写js时会有很多地方用到函数的回调。

最简单的例子就是:

<script language="javascript" type="text/javascript">
function doSomething(callback) {
if(typeof callback == "function") {
callback();
}
} 
 
function foo() {
alert("我是回调后执行的函数");
} 
 
doSomething(foo); /*正确*/
doSomething(function(){
alert("我是回调后执行的函数");
}); /*正确*/
doSomething("foo"); /* 这样是不行的,传入的是一个字符串,不是一个函数名 */
</script>

以上只能回调没有参数的(除法你事先知道回调的函数的参数),如果函数有未知的函数,就不能如此简单的调用了。

高级方法:

1、使用javascript的call方法

function doSomething(callback,arg1,arg2) {
callback.call(this,arg1,arg2);
}
function foo(arg1,arg2) {
alert(arg1+":"+arg2);
}
doSomething(foo,1,2); /* 弹出了1:2 */

2、使用javascript 的 apply方法

function doSomething(callback,args) {
callback.apply(window,args);
}
function foo(arg1,arg2) {
alert(arg1+":"+arg2);
}
doSomething(foo,[1,2,3]); /* 弹出了1:2 */

可以看成call和apply基本一样,区别就是call只能一个个传参数,apply只能把参数放数组里传进来。

他们的第一个参数都是作用域,比如上面传了this,表示就是和doSomething这个函数一样的作用域,当然你也可以传window,表示整个window的作用域。

3、apply的巧妙用法

apply也可以看作是函数的执行函数,就是用来执行某个函数的函数。所以你会发现,有时候用好apply,有很多原本繁杂的事情会变得如此简单。

比如数组的push方法使用apply来调用:

var arr1=[1,3,4];

var arr2=[3,4,5];

如果我们要把 arr2展开,然后一个一个追加到arr1中去,最后让arr1=[1,3,4,3,4,5]

arr1.push(arr2)显然是不行的。 因为这样做会得到[1,3,4,[3,4,5]]

我们只能用一个循环去一个一个的push(当然也可以用arr1.concat(arr2),但是concat方法并不改变arr1本身)

var arrLen=arr2.length
for(var i=0;i<arrLen;i++){
 arr1.push(arr2[i]);
}

自从有了Apply,事情就变得如此简单

Array.prototype.push.apply(arr1,arr2)

一行代码就解决了,原理能看的出来,Array.prototype.push是指数组的push函数,apply(arr1,arr2)说明arr1是作用域,就等同于是arr1调用了数组的push函数,

而且arr1的确就是个数组,所以可以调用,arr2表示入参的数组。所以,以上语句等同于:arr1.push(3,4,5)。(push函数支持传递多个入参,这也是这里可以使用apply的前提条件)

以上语句也可以写成:arr1.push.apply(arr1,arr2); 两者完全等效,因为arr1.push表示arr1的push函数,也就是数组的push函数。

如果使用call就是这样Array.prototype.push.call(arr1,arr2[0],arr2[1]...),显然还是apply合适。

要是你还问,那直接用arr1.push(3,4,5)不就行了,那已经暴露了你的智商,arr2又不是不可以变,下次不是[3,4,5]了呢。

还有获取数组中,最大的那个数字,也可以使用apply调用Math.max函数

var arr1=[1,3,4];

alert(Math.max.apply(window,arr1)); /* 作用域可以不是window,就算是null都行,Math.max.apply(this,arr1),Math.max.apply(null,arr1) */

4、工作中函数回调的实际例子

有了上面的基础,就能看的懂工作中封装好的js的回调函数了

背景:页面A需要使用页面B来选择某个项目,然后带回这个项目的信息给页面A,页面A根据这些信息丰富自己。

页面A:

noticeInfo = {
selectProject: function () {
var win = newsee.ui.window
win.show('项目列表', '../Project/ProjectSelectList.html?callback=noticeInfo.setProjectInfo', { size: win.winSizeType.big })
//在当前页面弹出框,框里面是另一个页面,地址后面带上需要回调的函数名
//注意这两个页面其实都是在一个页面里面的,并不是像window.open()那样出现了新窗口,所以两个页面的js都是可见的
},
setProjectInfo: function (obj) {
//回调函数,将选择好的项目对象传进来,然后丰富自己的页面
$('#projectName').val(obj.name)
$('#projectID').val(obj.id)
}
}

页面B:

function SelectBack() {
var callback = newsee.util.url.getQuery('callback'); //获取页面参数callback,这里获取到的是"noticeInfo.setProjectInfo",是个字符串
var arr = newsee.ui.grid.getSelectedBack('datagrid') //获取选择的项目,这个不用深究
if (!arr.length) {
return newsee.ui.window.alert('请选择项目!')
}
newsee.util.url.back(callback, arr[0]) //重点来了,这里执行回调,将需要回调的函数名和入参传进来,arr[0]就是选择的项目的对象的数组了(它也是个数组,里面就一个对象)
}

newsee.util.url.back函数如下:

back : function (funcName) {
// / <param name="funcName" type="String">返回时执行的方法,一般为重新绑定</param>
 
var isWindow = typeof $$winClose === 'function',// 是否为弹窗
args // 弹窗返回方法参数
 
if (isWindow) {// 弹窗的返回方法
$$winClose()
 
args = [].slice.call(arguments) //arguments大家应该都知道的吧,它可以用来获取函数的实参,它类似数组又不是数组,这句代码就是把它转换成数组,因为apply的入参需要是个数组才行
//args现在里面有两个元素,args[0]=callback,就是之前传进来的回调函数名,args[1]=arr[0],就是回调函数的入参
newsee.callFunc.apply(newsee, args) //执行 newsee.callFunc 函数,作用域就是newsee自己(等同于newsee自己调用callFunc函数),参数是args
} 
}

newsee.callFunc函数如下:

callFunc: function(funcName, arg) {
var func = typeof funcName === 'function' ? funcName : this.findItem(window, funcName) //上面我有提到过,doSomething("foo"); 传入的是一个字符串,不是一个函数名,所以无法执行
//同样的道理,现在funcName=args[0]=callback="noticeInfo.setProjectInfo",是个字符串,不能直接调用apply,需要变成函数
//这句话就是用来判断funcName是不是一个函数,如果不是,就在window作用域里根据funcName找到这个函数,然后赋给func
if (typeof func === 'function') {
//此时func已经是个函数了,就是页面A里定义的noticeInfo.setProjectInfo()
try {
return func.apply(window, arg) //执行需回调的函数,作用域依然是window,反正这个函数在window里肯定能找到,参数就是arg=args[1]=arr[0],即之前在页面B获取到的项目对象
}
catch (e) {
console.error(e)
}
}
}

ok,需回调的函数就这样被执行了,至于怎么根据字符串形式的函数名获取这个函数,看下面。

//findItem函数如下:
findItem: function(data, key) {
// / <summary>获取对象指定键的值</summary>
if (this.include(data, key)) { //data这里就是传进来的window,注意window就是一个对象,首先判断window对象里是否存在"noticeInfo.setProjectInfo"这个属性
return eval('data.' + key) //如果存在,就执行"data.noticeInfo.setProjectInfo",这样就获取到了这个函数了。(eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码)
}
}
//include函数如下:
include: function(data, key) {
// / <summary>判断对象是否存在键值</summary>
if (data == null || typeof data !== 'object' || !key || typeof key !== 'string') {
return false
}
var keys = key.split('.'),
item = data,
result = true
keys.forEach(function(k) {
if (item != null && typeof item === 'object' && k in item) {
//依次循环遍历,第一次item = data,那就是window这个对象,k="noticeInfo",window[noticeInfo]是存在的,因为在页面A里定义了noticeInfo这么一个对象
//第二次循环,item=window.noticeInfo,k="setProjectInfo",window.noticeInfo[setProjectInfo]也是存在的,因为在页面A里也定义了setProjectInfo这么一个函数
//这里没有第三次循环了,所以最后返回是true,说明window对象里存在"noticeInfo.setProjectInfo"这个属性,接下来使用eval()拿到它即可
item = item[k]
} else {
return result = false
}
})
 
return result
}

对eval() 函数也介绍一下:

eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。

返回值就是通过计算 string 得到的值(如果有的话)。如:

eval("x=10;y=20;document.write(x*y)") //
输出 200 document.write(eval("2+2")) //输出 4 var x=10 document.write(eval(x+17)) //输出 27

所以上面的eval('data.' + key)就是执行"data.noticeInfo.setProjectInfo"这个字符串,

因为data在这里就是指window,所以返回值就是window.noticeInfo.setProjectInfo()这个函数

其实可以在简单一点,根本没必要使用eval()来获取这个函数,因为在include函数里,item就已经是window.noticeInfo.setProjectInfo这个对象了,这个对象就是我们想要的函数。

(在js中函数也是对象,函数名就是这个函数的引用,就和地址差不多)

既然都拿到这个函数了,直接返回不就行了,所以上面的include()和findItem可以这样简化:

include: function(data, key) {
if (data == null || typeof data !== 'object' || !key || typeof key !== 'string') {
}else{
var keys = key.split('.'),
item = data,
result = true
keys.forEach(function(k) {
if (item != null && typeof item === 'object' && k in item) {
item = item[k]
} else {
result = false;
}
})
if(result)
return item
}
},
findItem: function(data, key) {
return this.include(data, key)

经过测试,发现这两个根据字符串形式的函数名获取函数的方法都可以达到一模一样的效果。

热心网友 时间:2022-04-22 11:11

function a(a1, b1){

console.log(a1, b1);

}

setTimeout(a, 3000, 'y', 'n')

首先,在控制台里,如果我输入 a,我只会得到一个代码内容的回调,如果我输入 a() 我就可以执行这个函数。而输入 'a()' 得到的是一个字符串 'a()'。

而 setTimeout 为了执行一个 'alert("计时器")' 这个字符串,就会像 eval 强制转换成可执行代码。

setInterval("test()",10)不是相当于传了一个函数执行结果而不是函数名

所以这句话话的理解是不正确的,你只是在执行一个字符串,他被强制转换成代码而已

追问恩,你说eval我就大概明白了,那setInterval(test,10)这样写其实也是对的是吧。
类似的,在行内标签中使用btn绑定事件使用"Submit()"字符串,和先使用js找到它再绑定函数名$('button').onclick=Submit,和上面的例子是类似原理,对吗?

追答其实 eval 的解释并不精确,只是为了便于理解这个执行的行为
实际上计时器执行字符串代码的时候,也是会包装成一个函数才去执行
onclick 属性的操作原理大径相同,onclick 属性也会把属性值传递到 click 事件上,形成一个 onclick 使用的函数体

setInterval(test,10) 才是标准的调用函数写法,这样不用再进行包装了,传参也更方便,如果需要给test 传参,就在时间后面追加

setInterval(test,10, 'arg1', 'arg2')

热心网友 时间:2022-04-22 12:29

甘肃是小学五年级,还能学到的,在小学12年级不能学到分数,还没有到学函数的年龄段

热心网友 时间:2022-04-22 14:03

在干数据集,调用就可以。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
手机垃圾清理使用哪个软件最好? 适合新手用的手机清理软件 苹果手机怎么把号码黑名单里? VUE视频相机使用教程 教你简单几步拍出大片 如何使用佳能相机录像 相机录像具体操作教程分享 羊头捣蒜做法图解 服刑期间领取养老金是否构成犯罪 狗狗胰腺炎最怕的三种蔬菜是什么 怎么喂食 银行卡给专业的贷款公司做流水有风险吗? 电话号可直接贷款吗,他知道我的实名电话,还用paos机刷了我的银行卡一... 华为怎么给应用加密 JavaScript里面的回调函数(callback)怎么传参数? 进入微信公众平台出现空白页的原因是什么? javascript的onload回调问题 javascript 中怎么给回调函数传值 手机微信部分公众号里的链接打不开 一片空白一直显示加载中。。。怎 谁能浅显、明白的解释JAVASCRIPT的回调函数? JavaScript的回调函数 白灼虾是怎么做的 白灼虾的三大做法 JavaScript:将函数作为参数传入,作为回调函数 关于javascript的回调函数同步问题 无水版白灼虾怎么做 javascript 回调函数 怎样制作白灼虾 javascript怎么写回调函数 请问白灼虾怎么做呢 苍蝇多怎么办 苍蝇太多怎么办 做好ppt后,当保存好后,因为没有改文件名把前面的ppt覆盖了,怎么找回啊 香芋地瓜丸炸多久会熟 微信链接打不开,大家知道怎么回事吗 华为怎么给软件加密锁 星期日可以打征信吗? javascript回调函数参数问题 我在点点网上链接的微信公众平台为什么是空白,打不开 熊立仲的事迹材料 请帮忙解释下面的Javascript代码,javascript初学者 关于创新的人物事迹有哪些? 请教关于javascript编写代码的优化方式 javascript 回调函数事情问题 JavaScript回调函数嵌套调用时,怎么调试比较方便 中国青年科技奖的广泛认可 JavaScript 回调函数怎么理解 人事档案 机械行业有杰出贡献的人物,事迹及他的影响 中国青少年科技创新奖的奖项介绍 中国青年科技奖&quot;算什么档次的奖 凤凰英才卡优惠政策 我在国海证券开了账户,科创板怎么开通? 国海证券科创板怎么开通?