【分享】一个JS小工具

猿敲月下码 2014-04-08 01:21:46
我们在开发中通常会遇到这样一种情况,比如我有一个函数
这个函数有个参数,函数的功能就是打印参数中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="点击" onclick="showName(Jim)">'
+'<input type="button" value="点击" onclick="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="点击" onclick="showName(Jim)">'
+'<input type="button" value="点击" onclick="showName(Lucy)">'
// 使用工具
+'<input type="button" value="使用工具" onclick="'+FunUtil.createFun(window,'showName',Jim)+'">'
+'<input type="button" value="使用工具" onclick="'+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="点击" onclick="showName(Jim)">'
+'<input type="button" value="点击" onclick="showName(Lucy)">'
// 使用工具,传入多个参数
+'<input type="button" value="使用工具" onclick="'+FunUtil.createFun(window,'showName',Jim,Lucy)+'">'
+'<input type="button" value="使用工具" onclick="'+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)" onclick="'+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

如果你想动态的生成按钮或<A>标签,按钮的onclick函数需要传入局部参数,此时可以考虑使用这个小工具
...全文
299 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
萧萧可乐 2014-04-09
  • 打赏
  • 举报
回复
15楼正解。。。 不过楼主可以积极思考问题 也不错
码无边 2014-04-09
  • 打赏
  • 举报
回复
js代码写了多多的,不知道你要做啥
scscms太阳光 2014-04-09
  • 打赏
  • 举报
回复
引用 14 楼 zzgzzg00 的回复:
没看明白你们想说什么 好像饶了一个非常大的圈子后又返回来一样 本来可以直接从a走到b的 非要从a走到c在走到d在走到f然后回到b
说明你是明白人,看出了问题根本。简单说就是一个访问作用域变量问题,本是一个闭包可以解决的事。
跳动de指尖 2014-04-09
  • 打赏
  • 举报
回复
闭包,你值得拥有。 个人感觉,这样用到的很少。
似梦飞花 2014-04-08
  • 打赏
  • 举报
回复
没看明白你们想说什么 好像饶了一个非常大的圈子后又返回来一样 本来可以直接从a走到b的 非要从a走到c在走到d在走到f然后回到b
scscms太阳光 2014-04-08
  • 打赏
  • 举报
回复
首先值得肯定的是研究这些解决问题。 但这都是表面现象,上面已经说明白了,不管什么作用域,你这是都移到统一的环境下执行。而并非是访问原对象或者函数。
猿敲月下码 2014-04-08
  • 打赏
  • 举报
回复
其实说白了就是允许我们在某个函数的外部作用域调用其自身作用域范围内的方法 有时为了防止重名,会把函数声明在另一个函数内部,如:
window.onload = function(){
	// 内部类
	function Inner(){
		var name = "Jim";
		this.showName = function(){
			alert(name);
		}
	}

	var inner = new Inner();
        // 通过<A>标签在外部作用域调用inner.showName()
	document.body.innerHTML += '<a href="javascript:'+FunUtil.createFun(inner,'showName')+'">Test</a><br>';
	document.body.innerHTML += '<a href="javascript:inner.showName()">Test2</a><br>'; // error
}
KeepSayingNo 2014-04-08
  • 打赏
  • 举报
回复
不错,谢谢楼主分享
猿敲月下码 2014-04-08
  • 打赏
  • 举报
回复
再举个例子 前台JS通过AJAX请求获取到商品列表,把商品名称展现到html上,点击商品名称显示商品详情
// 查看商品详情
function showDetail(goods){
	alert('厂商:'+goods.factory+',价格:'+goods.price);
}

window.onload = function(){
	// 从后台获取商品列表
	var goodsItems = [
		{name:'手机',factory:'NOKIA',price:1000}
		,{name:'电脑',factory:'Apple',price:2000}
	]

	// 生成html,点击查看商品详情
	for(var index in goodsItems){
		var goods = goodsItems[index];
		//document.body.innerHTML += '<a href="javascript:showDetail('+goods+')">'+goods.name+'</a><br>'; // error :(
		document.body.innerHTML += '<a href="javascript:'+FunUtil.createFun(window,'showDetail',goods)+'">'+goods.name+'</a><br>'
	}

}
当然可以传一个商品ID,通过商品ID查询商品信息再显示,这样的话就多了一步数据库操作了,显然是不好的
scscms太阳光 2014-04-08
  • 打赏
  • 举报
回复
    <script type="text/javascript">
        <!--
        //各函数私有变量不能互相访问,那我把他们都请到我家来不就解决问题。
        var FunUtil = (function(){
            return function(param,k){
                if(k){
                    showName(arr[--k]);//直接调用函数
                }else{
                    !this.arr&&(arr=[]);
                    arr.push(param);//存储变量,也是复制一下变量
                    return "FunUtil('',"+(arr.length)+")";
                }
            };
        })();
        function showName(obj){
            alert(obj.name);
        }
        window.onload = function(){
            var Jim = {name:"Jim"};
            var Lucy = {name:"Lucy"};
            var buttons = '<input type="button" value="没有工具" onclick="'+FunUtil(Jim)+'">'
                    +'<input type="button" value="没有工具" onclick="'+FunUtil(Lucy)+'">';
            document.body.innerHTML += buttons;
        };
        //-->
    </script>
没有那么神秘。
猿敲月下码 2014-04-08
  • 打赏
  • 举报
回复
引用 2 楼 xzy21com 的回复:
个人觉得没什么意义,你转了这么多弯一样是把局部变量必成全局变量来访问。
这个工具的用处就是可以在对象作用域之外来调用对象的方法,在动态拼接按钮的时候用到 如果事先在html写好标签,这个就用不到了
scscms太阳光 2014-04-08
  • 打赏
  • 举报
回复
说全局不准确,而是把局部的变量Jim与调用方法showName移植在FunUtil局部内触发。
socochen 2014-04-08
  • 打赏
  • 举报
回复
程序员就是挺能折腾的。呵呵!
亮子AI 2014-04-08
  • 打赏
  • 举报
回复
没明白用处是什么
scscms太阳光 2014-04-08
  • 打赏
  • 举报
回复
个人觉得没什么意义,你转了这么多弯一样是把局部变量必成全局变量来访问。
嘻哈大咖秀 2014-04-08
  • 打赏
  • 举报
回复
果断收藏了

87,907

社区成员

发帖
与我相关
我的任务
社区描述
Web 开发 JavaScript
社区管理员
  • JavaScript
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧