我们在开发中通常会遇到这样一种情况,比如我有一个函数
这个函数有个参数,函数的功能就是打印参数中name属性
function showName(obj){
alert(obj.name);
}
// 调用
var Tom = {name:"Tom"};
showName(Tom); // Tom
此时我有一个需求,需要动态生成一些按钮,点击按钮打印不同人的名字
<html>
<head>
<title></title>
<script type="text/javascript">
<!--
function showName(obj){
alert(obj.name);
}
window.onload = function(){
var Jim = {name:"Jim"};
var Lucy = {name:"Lucy"};
var buttons = '<input type="button" value="点击" οnclick="showName(Jim)">'
+'<input type="button" value="点击" οnclick="showName(Lucy)">';
document.body.innerHTML += buttons;
}
//-->
</script>
</head>
<body>
</body>
</html>
此时点击按钮会报错,因为Jim,Lucy是局部对象,按钮上的showName()函数访问不到,一种解决方法是把他们放到onload函数外面,成为全局对象.
显然这不是一种好方法.那么我们能不能用一个工具辅助下来实现呢
直接上代码吧
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script type="text/javascript">
<!--
var FunUtil = (function(){
var index = 0;
var handlerStore = []; // 存放方法句柄
var paramStore = []; // 存放参数
return {
// 创建方法字符串
// scope:作用域
// methodName:方法名,字符串格式
// param:参数
createFun:function(scope,methodName,param){
var currentIndex = index++; // 创建索引
// 根据索引储存方法句柄和方法参数
handlerStore[currentIndex] = function(param){
scope[methodName](param)
};
paramStore[currentIndex] = param;
return 'FunUtil._runFun('+currentIndex+')';
}
// 执行方法
// index:索引.根据这个索引找到方法句柄和方法参数
,_runFun:function(index){
var handler = handlerStore[index];
var param = paramStore[index];
handler(param);
}
};
})();
function showName(obj){
alert(obj.name);
}
window.onload = function(){
var Jim = {name:"Jim"};
var Lucy = {name:"Lucy"};
var buttons = '<input type="button" value="点击" οnclick="showName(Jim)">'
+'<input type="button" value="点击" οnclick="showName(Lucy)">'
// 使用工具
+'<input type="button" value="使用工具" οnclick="'+FunUtil.createFun(window,'showName',Jim)+'">'
+'<input type="button" value="使用工具" οnclick="'+FunUtil.createFun(window,'showName',Lucy)+'">';
document.body.innerHTML += buttons;
}
//-->
</script>
</head>
<body>
</body>
</html>
这里我写了一个工具类FunUtil,我们就使用它的一个方法
FunUtil.createFun(scope,methodName,param);
第一个参数是被调用函数的作用域,这里就是window
第二个参数是函数名字,字符串形式,这里就是''showName''
第三个参数是函数参数,这里是Jim,Lucy对象
----------------------------------
再来说下它的实现原理:
用两个数组分别来保存函数句柄和参数,然后我们通过数组的索引来获取对应的函数句柄和参数
注释已经写的很清楚了.
----------------------------------
当我写好这个工具的时候发现它并不完美,如果我们要传入多个参数呢
当时脑海中就想到了fun.apply(scope,arr);关于此方法可以网上搜索javascript的call apply
于是就有了下面的改进版
[推荐]
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script type="text/javascript">
<!--
/*
使用方法:
FunUtil.createFun(scope,'some_mothod_name',obj1);
FunUtil.createFun(scope,'some_mothod_name',obj1,obj2);
...
*/
var FunUtil = (function(){
var index = 0;
var handlerStore = []; // 存放方法句柄
return {
// scope:作用域
// methodName:方法名,字符串格式
// ...:参数可放多个
createFun:function(scope,methodName){
var currentIndex = index++; // 创建索引
var argu = []; // 用来存放多个参数
// 构建参数
for(var i=2,len=arguments.length;i<len;i++){
argu.push(arguments[i]);
}
// 把函数句柄存在数组里
handlerStore[currentIndex] = (function(scope,methodName,argu){
// 生成函数调用句柄
return function(){
scope[methodName].apply(scope,argu);
}
}(scope,methodName,argu));
return 'FunUtil._runFun('+currentIndex+')';
}
// 执行方法
// index:索引.根据这个索引找到执行函数
,_runFun:function(index){
var handler = handlerStore[index];
handler();// 该函数已经传入了参数
}
};
})();
function showName(obj1,obj2){
alert(obj1.name + " and " + obj2.name);
}
window.onload = function(){
var Jim = {name:"Jim"};
var Lucy = {name:"Lucy"};
var aa = {name:'aa'};
var bb = {name:'bb'};
var buttons = '<input type="button" value="点击" οnclick="showName(Jim)">'
+'<input type="button" value="点击" οnclick="showName(Lucy)">'
// 使用工具,传入多个参数
+'<input type="button" value="使用工具" οnclick="'+FunUtil.createFun(window,'showName',Jim,Lucy)+'">'
+'<input type="button" value="使用工具" οnclick="'+FunUtil.createFun(window,'showName',aa,bb)+'">' ;
document.body.innerHTML += buttons;
}
//-->
</script>
</head>
<body>
</body>
</html>
要说明的都写在注释里面了,核心方法就是scope[methodName].apply(scope,argu);
可能这个例子有点简单了,我们再来一个复杂点的,更贴近于实际项目的例子
我这里有个表格控件Grid,我要实现一个修改功能,在页面上生成修改列,点击修改,就可修改信息
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script type="text/javascript">
var FunUtil = (function(){
var index = 0;
var handlerStore = []; // 存放方法句柄
return {
// scope:作用域
// methodName:方法名,字符串格式
// ...:参数可放多个
createFun:function(scope,methodName){
var currentIndex = index++; // 创建索引
var argu = []; // 用来存放多个参数
// 构建参数
for(var i=2,len=arguments.length;i<len;i++){
argu.push(arguments[i]);
}
// 把函数句柄存在数组里
handlerStore[currentIndex] = (function(scope,methodName,argu){
// 生成函数调用句柄
return function(){
scope[methodName].apply(scope,argu);
}
}(scope,methodName,argu));
return 'FunUtil._runFun('+currentIndex+')';
}
// 执行方法
// index:索引.根据这个索引找到执行函数
,_runFun:function(index){
var handler = handlerStore[index];
handler();// 该函数已经传入了参数
}
};
})();
var Grid = function(){
this.row = [
{id:1,name:'Jack'}
,{id:2,name:'Tom'}
,{id:3,name:'Lucy'}
];
}
Grid.prototype.updateData = function(obj){
// 修改信息,可以跳转到另一个页面或者弹窗
alert('修改:'+obj.name+',主键ID:'+obj.id);
}
Grid.prototype.sendToBody = function(){
var html = '';
for(var i in this.row){
var obj = this.row[i];
html += (obj.id + ' ' + obj.name + ' <a href="javascript:void(0)" οnclick="'+FunUtil.createFun(this,'updateData',obj)+'">修改</a><br>');
}
document.body.innerHTML += html;
}
window.onload = function(){
var grid = new Grid();
grid.sendToBody();
}
</script>
</head>
<body>
</body>
</html>
这样,一个简单的表格就生成了
FunUtil.createFun(this,'updateData',obj); 这里的this指的是Grid对象,整句话的意思就是调用Grid的'updateData'方法,并传入obj