简单理财网|简单理解JavaScript闭包的笔记

时间:2019-05-09  来源:php函数  阅读:

## 1 JavaScript 函数允许你访问函数以外定义的变量

如果你清楚JavaScript的作用域链的概念,对于这条你就不难理解了.
大致的意思就是说,js寻找变量的定义会从最近的区域找起,本地找不到就往上一层区域

找,直到找到命名空间的顶端,在浏览器的世界里顶端就是window对象了。

 代码如下

example-1

var a_var_in_global_environment = "hello Closure"
function func(){
console.log(a_var_in_global_environment)
}
func()
hello Closure

example-2:

function func2(){
var var_in_function = "I"m a var in function";
return function(){
console.log(a_var_in_global_environment + " --and-- " + var_in_function) ;
}
}
func2()()
hello Closure --and-- I"m a var in function

 
## 2 即使外部函数已经返回,内部函数仍能记得外部函数定义的变量

这听起来有点不可思议,但却是是如此的。
就拿我们上面的example-2来说,func2 返回的值是一个函数(这也是所谓的高阶函数),而这个函数并不是立即执行的
,而这个函数内部保存着该函数作用域内几乎所有的参数和变量信息,即使外部函数返回了,它还是依然记得,
念念不忘。
所以,那些在其作用域内保持变量的函数,就可以称作为闭包

## 3 闭包可以更新外部变量的值


通过闭包可以做什么?


对这个问题的回答可能会令你惊讶--闭包什么都可以做。据我所知,闭包使得 ECMAScript 能够模仿任何事物,因此局限性在于设计和实现要模仿事物的能力。只是从字面上看可能会觉得这么说很深奥,下面我们就来看一些更有实际意义的例子。

例 1:为函数引用设置延时
闭包的一个常见用法是在执行函数之前为要执行的函数提供参数。例如:将函数作为 setTimout 函数的第一个参数,这在 Web 浏览器的环境下是非常常见的一种应用。

setTimeout 用于有计划地执行一个函数(或者一串 JavaScript 代码,不是在本例中),要执行的函数是其第一个参数,其第二个参数是以毫秒表示的执行间隔。也就是说,当在一段代码中使用 setTimeout 时,要将一个函数的引用作为它的第一个参数,而将以毫秒表示的时间值作为第二个参数。但是,传递函数引用的同时无法为计划执行的函数提供参数。

然而,可以在代码中调用另外一个函数,由它返回一个对内部函数的引用,再把这个对内部函数对象的引用传递给 setTimeout 函数。执行这个内部函数时要使用的参数在调用返回它的外部函数时传递。这样,setTimeout 在执行这个内部函数时,不用传递参数,但该内部函数仍然能够访问在调用返回它的外部函数时传递的参数:

 代码如下

function callLater(paramA, paramB, paramC){
    /* 返回一个由函数表达式创建的匿名内部函数的引用:- */

    return (function(){
        /* 这个内部函数将通过 - setTimeout - 执行,
         而且当它执行时它会读取并按照传递给
         外部函数的参数行事:www.111cn.net
        */
        paramA[paramB] = paramC;
    });
}

...

/* 调用这个函数将返回一个在其执行环境中创建的内部函数对象的引用。
 传递的参数最终将作为外部函数的参数被内部函数使用。
 返回的对内部函数的引用被赋给一个全局变量:-
*/

var functRef = callLater(elStyle, "display", "none");
/* 调用 setTimeout 函数,将赋给变量 - functRef -
的内部函数的引用作为传递的第一个参数:- */

hideMenu=setTimeout(functRef, 500);
例 2: 通过对象实例方法关联函数
回到顶部
许多时候我们需要将一个函数对象暂时挂到一个引用上留待后面执行,因为不等到执行的时候是很难知道其具体参数的,而先前将它赋给那个引用的时候更是压根不知道的。 (此段由 pangba 刘未鹏 翻译)

(luyy朋友的翻译_2008-7-7更新)很多时候需要将一个函数引用进行赋值,以便在将来某个时候执行该函数,在执行这些函数时给函数提供参数将会是有用处的,但这些参数在执行时不容易获得,他们只有在上面赋值给时才能确定。

(原文备考:There are many other circumstances when a reference to a function object is assigned so that it would be executed at some future time where it is useful to provide parameters for the execution of that function that would not be easily available at the time of execution but cannot be known until the moment of assignment.)

一个相关的例子是,用 JavaScript 对象来封装与特定 DOM 元素的交互。这个 JavaScript 对象具有 doOnClick、doMouseOver 和 doMouseOut 方法,并且当用户在该特定的 DOM 元素中触发了相应的事件时要执行这些方法。不过,可能会创建与不同的 DOM 元素关联的任意数量的 JavaScript 对象,而且每个对象实例并不知道实例化它们的代码将会如何操纵它们(即注册事件处理函数与定义相应的事件处理函数分离。译者注)。这些对象实例并不知道如何在全局环境中引用它们自身,因为它们不知道将会指定哪个全局变量(如果有)引用它们的实例。

因而问题可以归结为执行一个与特定的 JavaScript 对象关联的事件处理函数,并且要知道调用该对象的哪个方法。

下面这个例子使用了一个基于闭包构建的一般化的函数(此句多谢未鹏指点),该函数会将对象实例与 DOM 元素事件关联起来,安排执行事件处理程序时调用对象实例的指定方法,给象的指定方法传递的参数是事件对象和与元素关联的引用,该函数返回执行相应方法后的返回值。

 代码如下

/* 一个关联对象实例和事件处理器的函数。
它返回的内部函数被用作事件处理器。对象实例以 - obj - 参数表示,
而在该对象实例中调用的方法名则以 - methodName - (字符串)参数表示。
*/

function associateObjWithEvent(obj, methodName){
    /* 下面这个返回的内部函数将作为一个 DOM 元素的事件处理器*/

    return (function(e){
         /* 在支持标准 DOM 规范的浏览器中,事件对象会被解析为参数 - e - ,
           若没有正常解析,则使用 IE 的事件对象来规范化事件对象。
        */

        e = e||window.event;
        /* 事件处理器通过保存在字符串 - methodName - 中的方法名调用了对象
      - obj - 的一个方法。并传递已经规范化的事件对象和触发事件处理器的元素
      的引用 - this - (之所以 this 有效是因为这个内部函数是作为该元素的方法执行的)
     */

        return obj[methodName](e, this);
    });
}

/* 这个构造函数用于创建将自身与 DOM 元素关联的对象,
   DOM 元素的 ID 作为构造函数的字符串参数。
   所创建的对象会在相应的元素触发 onclick、
   onmouseover 或 onmouseout 事件时,
   调用相应的方法。
*/

function DhtmlObject(elementId){
    /* 调用一个返回 DOM 元素(如果没找到返回 null)引用的函数,
   必需的参数是 ID。 将返回的值赋给局部变量 - el -。
    */
    var el = getElementWithId(elementId);
     /* - el - 值会在内部通过类型转换变为布尔值,以便 - if - 语句加以判断。
   因此,如果它引用一个对象结果将返回 true,如果是 null 则返回 false。
   下面的代码块只有当 - el - 变量返回一个 DOM 元素时才会被执行。
    */
    if(el){
        /* 为给元素的事件处理器指定一个函数,该对象调用了
     - associateObjWithEvent - 函数。
     同时对象将自身(通过 - this - 关键字)作为调用方法的对象,
     并提供了调用的方法名称。 - associateObjWithEvent - 函数会返回
     一个内部函数,该内部函数被指定为 DOM 元素的事件处理器。
     在响应事件时,执行这个内部函数就会调用必要的方法。
     */
        el.onclick = associateObjWithEvent(this, "doOnClick");
        el.onmouseover = associateObjWithEvent(this, "doMouseOver");
        el.onmouseout = associateObjWithEvent(this, "doMouseOut");
        ...
    }
}
DhtmlObject.prototype.doOnClick = function(event, element){
    ... // doOnClick 方法体。.
}
DhtmlObject.prototype.doMouseOver = function(event, element){
    ... // doMouseOver 方法体。
}
DhtmlObject.prototype.doMouseOut = function(event, element){
    ... // doMouseOut 方法体。
}

这样,DhtmlObject 的任何实例都会将自身与相应的 DOM 元素关联起来,而这些 DOM 元素不必知道其他代码如何操纵它们(即当触发相应事件时,会执行什么代码。译者注),也不必理会全局命名空间的影响以及与 DhtmlObject 的其他实例间存在冲突的危险。

例子

 代码如下

几个有用的示例
//*************闭包uniqueID*************
uniqueID = (function(){ //这个函数的调用对象保存值
    var id = 0; //这是私有恒久的那个值
    //外层函数返回一个有权访问恒久值的嵌套的函数
    //那就是我们保存在变量uniqueID里的嵌套函数.
    return function(){return id++;};  //返回,自加.
})(); //在定义后调用外层函数. 
document.writeln(uniqueID()); //0
document.writeln(uniqueID()); //1
document.writeln(uniqueID()); //2
document.writeln(uniqueID()); //3
document.writeln(uniqueID()); //4
//*************闭包阶乘*************
var a = (function(n){
    if(n<1){ alert("invalid arguments"); return 0; }
    if(n==1){ return 1; }
    else{ return n * arguments.callee(n-1); }
})(4);
document.writeln(a);
function User( properties ) {    
    //这里一定要声明一个变量来指向当前的instance    
    var objthis = this;    
    for ( var i in properties ) {    
        (function(){    
                //在闭包内,t每次都是新的,而 properties[i] 的值是for里面的    
                var t = properties[i];    
                objthis[ "get" + i ] = function() {return t;};    
                objthis[ "set" + i ] = function(val) {t = val;};    
        })();     
    }    
}    
     
//测试代码    
var user = new User({    
    name: "Bob",    
    age: 44    
});    
     
alert( user.getname());    
alert( user.getage());    
     
user.setname("Mike");    
alert( user.getname());    
alert( user.getage());    
     
user.setage( 22 );    
alert( user.getname());    
alert( user.getage());

简单理财网|简单理解JavaScript闭包的笔记

http://m.bbyears.com/jiaocheng/50245.html

推荐访问:简单立体画 简单立体生日贺卡
相关阅读 猜你喜欢
本类排行 本类最新