本文摘自PHP中文网,作者(*-*)浩,侵删。
平常的前端开发工作中,编写js时会有很多地方用到函数的回调。最简单的例子就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <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方法
1 2 3 4 5 6 7 | function doSomething(callback,arg1,arg2) {
callback.call(this,arg1,arg2);
}
function foo(arg1,arg2) {
alert(arg1+ ":" +arg2);
}
doSomething(foo,1,2);
|
2、使用javascript 的 apply方法
1 2 3 4 5 6 7 | function doSomething(callback,args) {
callback.apply(window,args);
}
function foo(arg1,arg2) {
alert(arg1+ ":" +arg2);
}
doSomething(foo,[1,2,3]);
|
可以看成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本身)
1 2 3 4 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 | noticeInfo = {
selectProject: function () {
var win = newsee.ui.window
win.show( '项目列表' , '../Project/ProjectSelectList.html?callback=noticeInfo.setProjectInfo' , { size: win.winSizeType.big })
},
setProjectInfo: function (obj) {
$( '#projectName' ).val(obj.name)
$( '#projectID' ).val(obj.id)
}
}
|
页面B:
1 2 3 4 5 6 7 8 | function SelectBack() {
var callback = newsee.util.url.getQuery( 'callback' );
var arr = newsee.ui.grid.getSelectedBack( 'datagrid' )
if (!arr.length) {
return newsee.ui.window.alert( '请选择项目!' )
}
newsee.util.url.back(callback, arr[0])
}
|
newsee.util.url.back函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | back : function (funcName) {
var isWindow = typeof $ $winClose === 'function' ,
args
if (isWindow) {
$ $winClose ()
args = [].slice.call(arguments)
newsee.callFunc.apply(newsee, args)
}
}
|
newsee.callFunc函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | callFunc: function (funcName, arg) {
var func = typeof funcName === 'function' ? funcName : this.findItem(window, funcName)
if (typeof func === 'function' ) {
try {
return func.apply(window, arg)
}
catch (e) {
console.error(e)
}
}
}
|
ok,需回调的函数就这样被执行了,至于怎么根据字符串形式的函数名获取这个函数,看下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | findItem: function (data, key) {
if (this. include (data, key)) {
return eval ( 'data.' + key)
}
}
include : function (data, key) {
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 = item[k]
} else {
return result = false
}
})
return result
}
|
对eval() 函数也介绍一下:
eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。
返回值就是通过计算 string 得到的值(如果有的话)。如:
1 2 3 4 | eval ( "x=10;y=20;document.write(x*y)" )
document.write( eval ( "2+2" ))
var x=10
document.write( eval (x+17))
|
所以上面的eval('data.' + key)就是执行"data.noticeInfo.setProjectInfo"这个字符串,
因为data在这里就是指window,所以返回值就是window.noticeInfo.setProjectInfo()这个函数
其实可以在简单一点,根本没必要使用eval()来获取这个函数,因为在include函数里,item就已经是window.noticeInfo.setProjectInfo这个对象了,这个对象就是我们想要的函数。
(在js中函数也是对象,函数名就是这个函数的引用,就和地址差不多)
既然都拿到这个函数了,直接返回不就行了,所以上面的include()和findItem可以这样简化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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)
|
经过测试,发现这两个根据字符串形式的函数名获取函数的方法都可以达到一模一样的效果。
以上就是js函数的回调的详细内容,更多文章请关注木庄网络博客!
相关阅读 >>
老司机带你彻底搞懂js闭包各种坑
three.js使用gpu选取物体并计算交点位置
js 中排名前十的报错如何避免
js 判断值是否为数字
web学习之怎么使用纹理贴图
网页代码中js和css指的是什么
秒懂js的垃圾回收机制
html5和js绘制图片到canvas的方法
js内置对象 math 和 date 的详解
jquery表单插件jquery.form.js
更多相关阅读请进入《js》频道 >>
人民邮电出版社
本书对 Vue.js 3 技术细节的分析非常可靠,对于需要深入理解 Vue.js 3 的用户会有很大的帮助。——尤雨溪,Vue.js作者
转载请注明出处:木庄网络博客 » js函数的回调