js闭包(三)

场景一:采用函数引用方式的setTimeout调用

闭包的一个通常的用法是为一个在某一函数执行前先执行的函数提供参数。例如,在web环境中,一个函数作为setTimeout函数调用的第一个参数,是一种很常见的应用。

setTimeout将要执行的函数(或者一段javascript代码,但这不是我们要讨论的情况)作为它的第一个参数,下一个参数是需要延迟执 行的时间。如果一段代码想通过setTimeout来调用,那么它需要传递一个函数对象的引用来作为第一个参数。延迟的毫秒数作为第二个参数,但这个函数 对象的引用无法为将要被延迟执行的对象提供参数。

但是,可以调用另一个函数来返回一个内部函数的调用,将那个内部函数对象的引用传递给setTimeout函数。内部函数执行时需要的参数,在调用 外部函数时传递给它。setTimeout在执行内部函数时无需传递参数,因为内部函数仍然能够访问外部函数调用时提供的参数:

[javascript] view plain copy print?

  1. function callLater(paramA, paramB, paramC) {
  2. /*使用函数表达式创建并放回一个匿名内部函数的引用*/
  3. return (function () {
  4. /*
  5. 这个内部函数将被setTimeout函数执行;
  6. 并且当它被执行时,
  7. 它能够访问并操作外部函数传递过来的参数
  8. */
  9. paramA[paramB] = paramC;
  10. });
  11. }
  12. /*
  13. 调用这个函数将在它的执行上下文中创建,并最终返回内部函数对象的引用
  14. 传递过来的参数,内部函数在最终被执行时,将使用外部函数的参数
  15. 返回的引用被赋予了一个变量
  16. */
  17. var funcRef = callLater(elStyle, "display", "none");
  18. /*调用setTimeout函数,传递内部函数的引用作为第一个参数*/
  19. hideMenu = setTimeout(funcRef, 500);

场景二:将函数关联到对象的实例方法

有很多这样的场景:需要分配一个函数对象的引用,以便在未来的某个时间执行该函数。那么闭包对于为这个将要执行的函数提供引用会非常有帮助。因为该函数可能直到执行时才能够被访问。

有一个例子就是,一个javascript对象被封装用来参与一个特定DOM元素的交互。它有doOnClick、doMouseOver以及 doMouseOut方法。并且想在DOM元素上对应的事件被触发时执行这些方法。但是,可能会有关联着DOM元素的任意数量的javascript对象 被创建,并且单个的实例并不知道那些实例化它们的代码将如何处理它们。对象实例不知道怎样去“全局”地引用它们自己,因为它们不知道哪个全局变量(如果存 在)的引用将被分配给它们。

所以,问题是执行一个与特定javascript对象实例关联的事件处理函数,并且知道调用那个对象的哪个方法。

接下来的一个例子,在有元素事件处理的对象实例的关联函数上使用一个简单的闭包。通过传递event对象以及要关联元素的一个引用,为事件处理器分配不同的对象实例方法以供调用。

[javascript] view plain copy print?

  1. /*
  2. 一个给对象实例关联一个事件处理器的普通方法,
  3. 返回的内部函数被作为事件的处理器,
  4. 对象实例被作为obj参数,对象上将要被调用的方法名称被作为第二个参数
  5. */
  6. function associateObjWithEvent(obj, methodName) {
  7. /*返回的内部函数被用来作为一个DOM元素的事件处理器*/
  8. return (function (e) {
  9. /*
  10. 事件对象在DOM标准的浏览器中将被转换为e参数,
  11. 如果没有传递参数给事件处理内部函数,将统一处理成IE的事件对象
  12. */
  13. e = e || window.event;
  14. /*
  15. 事件处理器调用obj对象上的以methodName字符串标识的方法
  16. 并传递两个对象:通用的事件对象,事件处理器被订阅的元素的引用
  17. 这里this参数能够使用,因为内部函数已经被执行作为事件处理器所在元素的一个方法
  18. */
  19. return obj[methodName](e, this);
  20. });
  21. }
  22. /*
  23. 这个构造器函数,通过将元素的ID作为字符串参数传递进来,
  24. 来创建将自身关联到DOM元素上的对象,
  25. 对象实例想在对应的元素触发onclick、onmouseover、onmouseout事件时
  26. 对应的方法被调用。
  27. */
  28. function DhtmlObject(elementId) {
  29. /*
  30. 调用一个方法来获得一个DOM元素的引用
  31. 如果没有找到,则为null
  32. */
  33. var el = getElementWith(elementId);
  34. /*
  35. 因为if语句块,el变量的值在内部进行了类型转换,变成了boolean类型
  36. 所以当它指向一个对象,结果就为true,如果为null则为false
  37. */
  38. if (el) {
  39. /*
  40. 为了给元素指定一个事件处理函数,调用了associateObjWithEvent函数,
  41. 利用它自己(this关键字)作为被调用方法的对象,并且提供方法名称
  42. */
  43. el.onclick = associateObjWithEvent(this, "doOnClick");
  44. el.onmouseover = associateObjWithEvent(this, "doOnMouseOver");
  45. el.onmouseout = associateObjWithEvent(this, "doOnMouseOut");
  46. }
  47. }
  48. DhtmlObject.prototype.doOnClick = function (event, element) {
  49. //doOnClick body
  50. }
  51. DhtmlObject.prototype.doMouseOver = function (event, element) {
  52. //doMouseOver body
  53. }
  54. DhtmlObject.prototype.doMouseOut = function (event, element) {
  55. //doMouseOut body
  56. }

任何DhtmlObject的实例都能够将它们自身关联到它们感兴趣的DOM元素上去,不需要去担心这些元素将被其他的代码怎么处理,以及被全局命名空间“污染”或者与其他的DhtmlObject的实例产生冲突。

场景三:封装相关的功能集

闭包可以创建额外的scope,这可以被用来组合相关的或有依赖性的代码。用这种方式可以最大限度地减少代码干扰的危害。假设,一个函数被用来创建 一个字符串并且避免重复串联的操作(比如创建一系列的中间字符串)。一个想法是,用一个数组来顺序存储字符串的一部分,然后使用 Array.prototype.join方法输出结果(使用一个空字符串作为它的参数)。数组将扮演着输出缓冲区的角色,但局部定义它又将会导致它在函 数的每次执行时再次创建。如果这个数组只是作为唯一的变量被分配给每一个函数调用,这将会有点小题大做。

一个解决方案是将数组提升为全局变量,让它不需要被再次创建也能够再次使用。但结果并不是想的那么简单,另外,一个全局变量关联这使用缓冲数组的函 数,那将会有第二个全局属性(函数本身也是window对象的属性)关联这个数组,这将让代码失去一定的可控性。因为如果将它使用在其他地方。这段代码的 创建者不得不记住包含函数的定义以及数组的定义逻辑。它也使得代码不那么容易与其他代码整合,因为将从原来只需要确定函数名是否在全局命名空间中唯一,变 成有必要确定和该函数关联的数组的名称是否在全局命名空间中保持唯一。

一个闭包可以让缓冲数组关联(干净地包含)它依赖的函数,并且同时保持缓冲数组的属性名称,像被分配在全局空间中一样,同时能够避免名称冲突以及代码交互干扰的危险。

这里有一招就是通过执行一个内联的函数表达式创建一个额外的执行上下文,让那个函数表达式返回一个内联的函数,该函数被外部代码使用。缓冲数组被定 义在内联执行的函数表达式中,作为一个局部变量。它仅被调用一次,所以该数组只被创建一次。但是对于依赖它的函数来说该数组是一直可访问的,并且可被重用 的。

接一下的代码创建了一个函数,将返回一个HTML字符串,其中的一部分是不变的,但那些不变的字符串需要被穿插进作为参数传递进来的变量中。

一个内联执行的函数表达式返回了内部函数对象的一个引用。并且分配了一个全局变量,让它可以被作为一个全局函数来调用。而缓冲数组作为一个局部变量被定义在外部函数表达式中。它没有被扩展到全局命名空间中,并且无论函数什么时候使用它都不需要被再次创建。

[javascript] view plain copy print?

  1. /*
  2. 定义一个全局变量:getImgInPositionedDivHtml
  3. 被赋予对外部函数表达式一次调用返回的一个内部函数表达式
  4. 内部函数返回了一个HTML字符串,代表一个绝对定位的DIV
  5. 包裹这一个IMG元素,而所有的变量值都被作为函数调用的参数
  6. */
  7. var getImgInPositionedDivHtml = (function () {
  8. /*
  9. buffAr 数组被定义在外部函数表达式中,作为一个局部变量
  10. 它只被创建一次。数组的唯一实例对内部函数是可见的,
  11. 所以它可以被用于每一次的内部函数执行
  12. 空字符串仅仅被用来作为一个占位符,它将被内部函数的参数代替
  13. */
  14. var buffAr = [
  15. ‘<div id="‘,
  16. ‘‘,   //index 1, DIV ID attribute
  17. ‘" style="position:absolute;top:‘,
  18. ‘‘,   //index 3, DIV top position
  19. ‘px;left:‘,
  20. ‘‘,   //index 5, DIV left position
  21. ‘px;width:‘,
  22. ‘‘,   //index 7, DIV width
  23. ‘px;height:‘,
  24. ‘‘,   //index 9, DIV height
  25. ‘px;overflow:hidden;\"><img src=\"‘,
  26. ‘‘,   //index 11, IMG URL
  27. ‘\" width=\"‘,
  28. ‘‘,   //index 13, IMG width
  29. ‘\" height=\"‘,
  30. ‘‘,   //index 15, IMG height
  31. ‘\" alt=\"‘,
  32. ‘‘,   //index 17, IMG alt text
  33. ‘\"><\/div>‘
  34. ];
  35. /*
  36. 返回一个内部函数对象,他是函数表达式执行返回的结果
  37. */
  38. return (function (url, id, width, height, top, left, altText) {
  39. /*
  40. 分配各种参数给对应的数组元素
  41. */
  42. buffAr[1] = id;
  43. buffAr[3] = top;
  44. buffAr[5] = left;
  45. buffAr[13] = (buffAr[7] = width);
  46. buffAr[15] = (buffAr[9] = height);
  47. buffAr[11] = url;
  48. buffAr[17] = altText;
  49. /*
  50. 返回连接每个元素后创建的字符串
  51. */
  52. return buffAr.join(‘‘);
  53. });
  54. })();

如果一个函数依赖另一个或几个函数,但那些其他的函数并不期望与任何其他的代码产生交互。那么这个简单的技巧(使用一个对外公开的函数来扩展那些函数)就可以被用来组织那些函数。

时间: 2024-12-06 06:23:44

js闭包(三)的相关文章

js闭包浅了解

js闭包浅理解 要理解闭包,得先知道js的变量作用域,在js中,有两种变量作用域: 全局作用域 局部作用域 一.在函数内可以访问全局变量 比如,下面的例子: <!--lang:js--> <script> var n = 100; function f1(){ console.log(n); } f1()//返回100 </script> 上面的例子很简单,下面是另一种情况. 二.在函数外无法读取函数内的局部变量 还是一个小例子: <!--lang:js-->

js闭包的作用

js闭包的用途详解 js闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中.具体怎么理解呢,各位看官请仔细看好下文 我们来看看闭包的用途.事实上,通过使用闭包,我们可以做很多事情.比如模拟面向对象的代码风格:更优雅,更简洁的表达出代码:在某些方面提升代码的执行效率. 1 匿名自执行函数 我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误用

浅谈JS闭包中的循环绑定处理程序

初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript的闭包特性. 前几天工作中写前端js代码时,遇到了遍历元素给它添加单击事件. (PS:之前也在<jQuery基础教程>第四版中看过讲循环绑定处理程序的内容,当时估计也没怎么用心看,所以没记起来.) 大神要是知道这类情况,可以关掉窗口,写这些主要是给像我一样的小白看的,谢谢! 先贴上错误的例子让大家

关于JS闭包,作者不详(转)

说明:本文由两篇文章结合而成,系从他人笔记中转过来的, 具体作者不详.因为觉得不错,遂共享之.如有侵权,立删致歉. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 fun

js闭包(closure),个人理解

一.闭包概念理解 各种专业文献上对js"闭包"(closure)定义非常抽象,贼难看懂.我的理解是,闭包就是能够读取某函数内部变量的函数.由于在Javascript语言中只有在函数内部的函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数".所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁. 二.用途 闭包可以用在很多地方.但它最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中. 三.使

js闭包之我见

很久前的一个问题终于得以解决,内心是无比喜悦的,不多说,先上代码: 1 function test(){ 2 for(var i=0;i<5;i++){ 3 window.onclick=function(){ 4 alert(i); 5 } 6 } 7 } 8 test() 原意是点击第一下弹出i的值0,点击第二下弹出i的值1,一直到第五次点击弹出4.想象是美好的,现实却不按常规出牌,无论点击多少次,alert出来的值都是5.后来接触了js闭包,才知道原来是它搞的鬼.看了不少相关书籍和博客后,

大部分人都会做错的经典JS闭包面试题

大部分人都会做错的经典JS闭包面试题 目录 由工作中演变而来的面试题 JS中有几种函数 创建函数的几种方式 三个fun函数的关系是什么? 函数作用域链的问题 到底在调用哪个函数? 后话 由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,

javascript深入理解js闭包(看了挺多的,感觉这篇比较透彻)

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function

探讨js闭包

背景:爱就要大胆说出来,对于编程我只想说,喜欢就大胆写出来.喜欢却不行动那就意味着失败.所以,对于在研究编程的猿们,我对同伴们说,大胆的学,大胆的写.呵呵,说这些其实无非是给我自己点动力,写下去的勇气. 今天写的是js闭包,这一个一直被我忽视的美.怎么说呢,其实之前我这人特别讨厌用闭包,为什么呢,因为习惯了写后台代码的人都知道,函数内部是不可以嵌套定义函数的.所以这一思想我之前一直没有转变,也感觉js的这样写法很“特别.很奇怪.甚至感觉没有必要,这也是我不想用的根本原因了.背景交代完毕. 额..

java内部类之js闭包

前言: 今天写了一个关于Java内部的博客,在内部类的最后一点中谈到了Java闭包的概念,他是这样定义闭包的:闭包是一个可调用的对象,它记录了一些信息,这些信息来自创建它的作用域.结合Java的内部类可以很好的理解这一点(如有需要可参考https://www.cnblogs.com/jinliang374003909/p/10351877.html).突然之间想到js中的闭包,一直都无法很好的理解,故借此又看了一下js中的闭包,对我个人而言,感悟良多,借此也与大家分享一下,希望可以帮助大家,并一