实例说明js闭包的使用(只是使用级别)

土匪强 发布于 2012/02/12 11:52
阅读 3K+
收藏 13
昨天接触到的一些问题让我对js闭包的使用有了更深刻的理解。 
现在我将问题的“上下文”详细说一下: 
我要实现一个页面,让管理员对用户的权限进行管理,页面如下: 


 

 

 


我要实现的效果是:当管理员单击下拉列表对用户的审核状态进行更改之后,将要修改的用户id和目前审核状态发送至后台,在后台处理完之后将信息反馈给前端,并进行一些提示,效果如图:

 

 

 


将提示信息显示在被处理用户信息同一行的一个单元格里面。
简单的思路:用jquery.post也好xmlhttprequest.open也好,当管理员对某一行的审核状态进行修改的时候,收集用户编号和修改后的审核状态,发送给后台,后台将处理的结果反馈回来,当前端得到后台的反馈之后,执行事先定义的回调函数,进行相应的提示操作:

jQuery.post(
    url,
    {id:2,state:good},
    function(){
      //more cord……
    }
);


问题的关键来了,从服务器反馈的信息可以知道要显示操作成功还是操作失败,但是,你要在表格里面显示提示信息,至少要知道提示信息要显示在第几行吧,然而后台并不是万能的,这么“前端”的问题,就不要为难后台了。
也许你会说,那就定义一个js的全局变量,记录管理员操作的记录是哪一行的,到时候后台反馈了就能直接定位显示了,好吧,这个暂时是解决了,假如,这个管理员的手很快,而后台的反应很慢,管理员处理了五六个,后台才返回其中一个的处理信息呢?也许你又会说,定一个数组吧,存放被处理的行号,然后要求后台在返回处理结果的同时也返回id,然后balabalabala捣鼓一下就能显示了⊙﹏⊙b,咱能不能有点追求呀。

好了引出我要说的主题js闭包,上面的问题,在我看来比较顺眼的处理方式是:在每一个给jquery.post或xmlhttprequest.open注册回调函数的时候,将对应要显示提示信息的label或是span也一并传进去,执行回调的时候就很方便啦,于是乎有了以下面的初步成果:

function callback(value){
 alert(value);
}

jQuery.post(
    url,
    {id:2,state:good},
    callback("helloworld");
);


但是发现只要jQuery.post方法一执行,就马上弹出helloworld的对话框,严重不符合我们的需求,我们要的效果是等到如后台返回信息的时候才弹出helloworld的对话框,照我的理解,诸如函数名后面加个括号:functionName(),就等于是函数调用,执行到那里就直接调用了(个人理解,仅供参考),好了虽然这个方法不行,但大概明确了目标,要将一个值传给一个函数,而在传的同时这个函数得等到我们需要的时候才能执行,使用闭包就能实现这个效果了:

function parent_fun(value){
 var name=value;
 function child(){
  alert(name);
 }
 return child; //注意这里将child函数返回 
}

function test(){
 var fun=parent_fun("helloword");
 fun();
}


parent_child函数定义了一个局部变量name和一个函数child,然后将child函数返回出去,在执行了parent_child("helloword")之后就会得到返回的child函数,同时child函数的alert(name);这句中的name还是指向父函数parent_child的局部变量name,而在child函数被返回的时候,child并没有被执行,这个过程下来之后就大概达到了我们之前的目的了,child函数中使用的参数是我们预先传给parent_child的,而且child函数的执行也是可以由我们控制的。 
这其中的原理我不是很清楚,  就引用网上其他网友的一段话吧:
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

原文地址
  (上面对js闭包有详细的讲解 ,我对这方面理解还不是很深入)
现在将全部代码贴出来: 
首先是table的html代码: 
    
<table > //方便起见只有一行
	<tr>
	    <th>编号</th>  <th>用户名</th>   <th>审核状态</th>
	</tr>
	<tr>
	    <td>1</td>
	    <td>jim</td>
	    <td>
		<select onchange="change(this)">		
		    <option value=0>审核通过</option>
		    <option value=1>审核不通过</option>
		</select>
                <span></span>   //这里是放置提示信息的地方!!!!!
	    </td>
	</tr>	
</table>


然后是js代码: 
//当select的onchange事件触发之后执行的函数
function change(mySelect){
     var myTr=$(mySelect).parent().parent();//获取到select所在的行tr元素
     var mySapn=myTr.find("span")[0]; //查找到所在行的span元素
     var id=myTr.find("td")[0].innerHTML; //获取用户编号
     var shenhe=mySelect.value; //获取审核状态
     var callback=callback_parent(mySelect); //将要显示提示信息的span传进去,得到回调函数
     jQuery.post(
                    url, //请求的url
                    {ID:id,ShenHe:shenhe}, //请求的参数
                    function(data){      //回调函数
                        callback(data);
                    }
                );
}

//获取jQuery.post中后台数据回传后执行的回调函数
function callback_parent(arg){
    var mySpan=arg;
    function callback(msg){
        $(mySpan).attr("innerHTML",msg);//根据传入的参数,更改对应span的显示信息
    }
    return callback; //将函数返回
}



还有一种方式可以实现上面的效果,试想,开辟一个地方,可以容纳一些属性,和方法,然后需要的时候可以调用这些方法同时使用那些属性,这些特征很像对象,在js中也能自己定义对象,比如: 
var o={
  name:"雷锋",
  info:"助人为乐"
}


那么上面也能用下面的方法来实现: 
//当select的onchange事件触发之后执行的函数
function change(mySelect){
     var myTr=$(mySelect).parent().parent();//获取到select所在的行tr元素
     var mySapn=myTr.find("span")[0]; //查找到所在行的span元素
     var id=myTr.find("td")[0].innerHTML; //获取用户编号
     var shenhe=mySelect.value; //获取审核状态     
     

     var callbackObject={       //这里是不一样的地方
            callback:function(msg){
                span_:mySpan,
                $(this.span_).attr("innerHTML",msg); //这里使用this.span_调用
            }
          }; 

     
     jQuery.post(
                    url, //请求的url
                    {ID:id,ShenHe:shenhe}, //请求的参数
                    function(data){      //回调函数
                        callbackObject(data); //这里没有什么变化
                    }
                );
}


但是这样做的缺点也是明显的,就是不能重用,如果你有很多地方都要使用这个回调函数,那么每个地方都要重新写过了,如果只是临时使用,这个也不失为一个好方法 
加载中
0
mark35
mark35
不错。闭包的本质就是snapshot,保留现场环境以便以后调用操作。
0
MUTEX
MUTEX
jquery ajax 有个参数 context 可以更优雅地解决这个问题。
0
土匪强
土匪强

引用来自“MUTEX”的答案

jquery ajax 有个参数 context 可以更优雅地解决这个问题。
哈哈,果然不错,谢谢
0
hylent
hylent
我解决的思路是 给你的每个select及其对应的span起一个相同后缀的id,在异步请求之前和之后都可以方便操作字符串来方便找到想找的。
0
土匪强
土匪强

引用来自“hylent”的答案

我解决的思路是 给你的每个select及其对应的span起一个相同后缀的id,在异步请求之前和之后都可以方便操作字符串来方便找到想找的。
嗯嗯,这个可以,不过一般来说表格里面的数据都是动态加载的,那么就要动态生成id啦,逻辑就复杂了
0
atearsan
atearsan

对于功能的实现感觉没你说的复杂...

我一般的做法:

function change(myselect) {

  var _this = this;

  ....

  $.getJSON(url,data,function(json) {

      $(_this).after(json.msg); // span标签都省了, 哈哈

  });

}

// 没具体调试... 不过大体差不多了

返回顶部
顶部