小凯 你java里面用过闭包么,[Java教程]你必须知道的【闭包】陷阱和案例

[Java教程]你必须知道的【闭包】陷阱和案例

0

2012-02-27 08:00:05

闭包

In some languages, a closure may occur when a function is defined within another function, and the inner function refers to local variables of the outer function.

At run-time, when the outer function executes, a closure is formed, consisting of the inner function’s code and references (the upvalues) to any variables of the outer function required by the closure.

翻译:

在一些编程语言当中,闭包发生  :在一个函数内部定义了另外一个函数,并且内部的函数引用了外部函数的本地变量。

在运行的时候,当外部函数执行,这个时候形成了一个闭包,由内部函数的代码和对外部函数任意变量引用组成,这写引用都依赖于此闭包。 // ECMAScriptvar f, g;function foo() {var x = 0;f = function () { return ++x; };g = function () { return --x; };x = 1;alert('inside foo, call to f(): ' + f()); // "2"}//外部函数执行,这个时候形成了闭包foo();//因为有了闭包,所以才访问到了 foo中的xalert('call to g(): ' + g()); // "1"//因为有了闭包,所以才访问到了 foo中的xalert('call to f(): ' + f()); // "2"

javascript闭包陷阱与案例

在很多时候,由于内部函数的变量覆盖了闭包的变量,我们如果需要引用外部函数同名的变量,需要通过执行匿名函数,不外部函数的变量作为参数传递进来。如下所示:(function(out_xxx){//这里面就可以使用out_xxx}(xxx))

可以看得出来,使用这种方式最多的地方是在我们定义对象的时候:(function (window) {var MyObject = function () {this.initialize();}var p = DisplayObject.prototype;p.initialize = function () {}window.MyObject = MyObject;} (window));

这样定义对象有两个好处:1.避免污染外部变量

2.传递参数的形式减少作用域查找

javascript为我们埋了很多坑,在许多场景下,我们需要利用以上的形式去解决问题,下面依依列出。

场景1 :

如下所示,我需要在cc方法中调用到外面的name:var bb, cc;function aa() {var name = "当耐特";bb = function () {var name = "砖家";cc = function () {var name = "张磊";alert(name);}}}aa();bb();cc();//输出 “张磊”因为内部的函数定义的变量覆盖了外部函数的变量,所以结果输出“张磊”。

解决办法:var bb, cc;function aa() {var name = "当耐特";(function (aa_name) {bb = function () {var name = "砖家";(function (bb_name, aa_name) {cc = function () {var name = "张磊";alert(aa_name);alert(bb_name);alert(name);}})(name, aa_name);}})(name);}aa();bb();cc();//输出“当耐特” “砖家” “张磊”

真实案例:

记得上周,我的一个同事(实习生),对下面一段代码产生疑惑,所以咨询我。如下所示:$("#dialog-form").dialog({autoOpen: false,height: 300,width: 350,modal: true,buttons: {"Create an account": function () {var bValid = true;allFields.removeClass("ui-state-error");bValid = bValid && checkLength(name, "username", 3, 16);bValid = bValid && checkLength(email, "email", 6, 80);bValid = bValid && checkLength(password, "password", 5, 16);if (bValid) {$.ajax({type: "POST",url: "xxxxx.aspx",data: "name=xxxxx&email=xxxxx&password=xxxx"}).done(function (msg) {alert("Data Saved: " + msg);$(this).dialog("close");});}},Cancel: function () {$(this).dialog("close");}},close: function () {allFields.val("").removeClass("ui-state-error");}});

这里用的是JqueryUI的dialog插件。详见: http://jqueryui.com/demos/dialog/#modal-form

bc91bb04e6e9c61e24c974e4440db8f2.gif

他想要的效果是点击create发起一个异步提交,然后在回调的时候关闭弹出层。令他困惑的地方是,弹出层关闭不了。

他抱怨着说:Cancel: function () {$(this).dialog("close");}

我的cancel都能关闭。为什么if (bValid) {$.ajax({type: "POST",url: "xxxxx.aspx",data: "name=xxxxx&email=xxxxx&password=xxxx"}).done(function (msg) {alert("Data Saved: " + msg);$(this).dialog("close");});}

这里面的$(this).dialog("close")为什么就不能关闭?

这是一个很典型的场景,解决办法:if (bValid) {(function (outThis) {$.ajax({type: "POST",url: "xxxxx.aspx",data: "name=xxxxx&email=xxxxx&password=xxxx"}).done(function (msg) {alert("Data Saved: " + msg);$(outThis).dialog("close");});}}(this))},

场景2---循环中的内部函数function TestObj(name) {this.name = name;}var objs = [];var obj;function test() {for (var i = 0; i < 100; i++) {var name = "张磊" + i;obj = new TestObj(name);obj.printName = function () {console.log(obj.name);}objs.push(obj);}}//外部函数执行,闭包形成。内部函数obj.printName中的obj全部指向最后一次new TestObj(name);test();//所以这里会输出100次-----"张磊99"for (var i in objs) {objs[i].printName();}

解决办法function TestObj(name) {this.name = name;}var objs = [];var obj;function test() {for (var i = 0; i < 100; i++) {var name = "张磊" + i;obj = new TestObj(name);(function (target) {obj.printName = function () {console.log(target.name);}} (obj))objs.push(obj);}}test();for (var i in objs) {objs[i].printName();}

真实案例:// create and populate the screen with random daisies:for(var i = 0; i < 100; i++){bitmap = new Bitmap(image);container.addChild(bitmap);bitmap.x = canvas.width * Math.random()|0;bitmap.y = canvas.height * Math.random()|0;bitmap.rotation = 360 * Math.random()|0;bitmap.regX = bitmap.image.width/2|0;bitmap.regY = bitmap.image.height/2|0;bitmap.scaleX = bitmap.scaleY = bitmap.scale = Math.random()*0.4+0.6;bitmap.name = "bmp_"+i;// wrapper function to provide scope for the event handlers:(function(target) {bitmap.onPress = function(evt) {// bump the target in front of it's siblings:container.addChild(target);var offset = {x:target.x-evt.stageX, y:target.y-evt.stageY};// add a handler to the event object's onMouseMove callback// this will be active until the user releases the mouse button:evt.onMouseMove = function(ev) {target.x = ev.stageX+offset.x;target.y = ev.stageY+offset.y;// indicate that the stage should be updated on the next tick:update = true;}}bitmap.onMouseOver = function() {target.scaleX = target.scaleY = target.scale*1.2;update = true;}bitmap.onMouseOut = function() {target.scaleX = target.scaleY = target.scale;update = true;}})(bitmap);}

这是Easeljs官网demo的一段代码,因为内部函数不会立即执行,所以当执行的时候,内部函数引用外部函数变量的时候,该变量已经被外层的for循环覆盖了N次,所以要通过上面的方式来解决这个问题。

小结

这是javascript蹩脚的一个方面,除了这个,还有javascript的 getter和setter也是该语言语法特性中令人憋屈的地方。

我相信经过人类的不懈努力,总有那么一天:static 、namespace、interface、private、protected 、class、enum·············都能成为javascript的关键字,而不是五花八门的技巧。相信那个时候,对应这些关键字的所有 文章 笔记  心得  日记 技巧 随笔 后门  都将沉尸谷底、永无翻身之日。

本文网址:http://www.shaoqun.com/a/22434.html

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们:admin@shaoqun.com。

0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值