??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲大尺度无码无码专区,色噜噜的亚洲男人的天堂,www亚洲精品久久久乳http://www.tkk7.com/xyzroundo/category/34594.html执着......坚持......zh-cnFri, 14 Nov 2008 18:07:25 GMTFri, 14 Nov 2008 18:07:25 GMT60Javascript 闭包http://www.tkk7.com/xyzroundo/articles/240618.htmlxyzxyzFri, 14 Nov 2008 16:14:00 GMThttp://www.tkk7.com/xyzroundo/articles/240618.htmlhttp://www.tkk7.com/xyzroundo/comments/240618.htmlhttp://www.tkk7.com/xyzroundo/articles/240618.html#Feedback0http://www.tkk7.com/xyzroundo/comments/commentRss/240618.htmlhttp://www.tkk7.com/xyzroundo/services/trackbacks/240618.htmlJavascript 闭包

译Q?a title="Z漫笔" >Z漫笔
链接Q?a title="原文链接" >http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html
英文原版Q?a >http://jibbering.com/faq/faq_notes/closures.html

?/a>

Closure
所?#8220;闭包”Q指的是一个拥有许多变量和l定了这些变量的环境的表辑ּQ通常是一个函敎ͼQ因而这些变量也是该表达式的一部分?

闭包?ECMAScript QJavaScriptQ最强大的特性之一Q但用好闭包的前提是必须理解闭包。闭包的创徏相对ҎQh们甚至会在不l意间创建闭包,但这些无意创建的闭包却存在潜在的危害Q尤其是在比较常见的览器环境下。如果想要扬镉K短地使用闭包q一Ҏ,则必M解它们的工作机制。而闭包工作机制的实现很大E度上有赖于标识W(或者说对象属性)解析q程中作用域的角艌Ӏ?

关于闭包Q最单的描述是 ECMAScript 允许使用内部函数Q-卛_数定义和函数表达式位于另一个函数的函数体内。而且Q这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这L内部函数在包含它们的外部函数之外被调用时Q就会Ş成闭包。也是_内部函数会在外部函数q回后被执行。而当q个内部函数执行Ӟ它仍然必需讉K其外部函数的局部变量、参C及其他内部函数。这些局部变量、参数和函数声明Q最初时Q的值是外部函数q回时的|但也会受到内部函数的影响?

遗憾的是Q要适当地理解闭包就必须理解闭包背后q行的机Ӟ以及许多相关的技术细节。虽然本文的前半部分q没有涉?ECMA 262 规范指定的某些算法,但仍然有许多无法回避或简化的内容。对于个别熟悉对象属性名解析的h来说Q可以蟩q相关的内容Q但是除非你寚w包也非常熟悉Q否则最好是不要跌下面几节?

对象属性名解析

ECMAScript 认可两类对象Q原生(NativeQ对象和宿主QHostQ对象,其中宿主对象包含一个被UCؓ内置对象的原生对象的子类QECMA 262 3rd Ed Section 4.3Q。原生对象属于语aQ而宿d象由环境提供Q比如说可能是文档对象、DOM {类似的对象?

原生对象h松散和动态的命名属性(对于某些实现的内|对象子cd而言Q动态性是受限的-Q但q不是太大的问题Q。对象的命名属性用于保存|该值可以是指向另一个对象(ObjectsQ的引用Q在q个意义上说Q函C是对象)Q也可以是一些基本的数据cdQ比如:String、Number、Boolean、Null ?Undefined。其中比较特D的?Undefined cdQ因为可以给对象的属性指定一?Undefined cd的|而不会删除对象的相应属性。而且Q该属性只是保存着 undefined 倹{?

下面要介l一下如何设|和d对象的属性|q最大程度地体现相应的内部细节?

值的赋予

对象的命名属性可以通过命名属性赋值来创徏Q或重新赋倹{即Q对于:

var objectRef = new Object(); //创徏一个普通的 javascript 对象?/span>

可以通过下面语句来创建名?“testNumber” 的属性:

objectRef.testNumber = 5;
/* - ? */
objectRef["testNumber"] = 5;

在赋g前,对象中没?#8220;testNumber” 属性,但在赋值后Q则创徏一个属性。之后的M赋D句都不需要再创徏q个属性,而只会重新设|它的|

objectRef.testNumber = 8;
/* - ? */
objectRef["testNumber"] = 8;

E后我们会介l,Javascript 对象都有原型QprototypesQ属性,而这些原型本w也是对象,因而也可以带有命名的属性。但是,原型对象命名属性的作用q不体现在赋值阶Dc同P在将Dl其命名属性时Q如果对象没有该属性则会创命名属性,否则会重设该属性的倹{?

值的d

当读取对象的属性值时Q原型对象的作用便体现出来。如果对象的原型中包含属性访问器Qproperty accessorQ所使用的属性名Q那么该属性的值就会返回:

/* 为命名属性赋倹{如果在赋值前对象没有相应的属性,那么赋值后׃得到一个:*/
objectRef.testNumber = 8;
/* 从属性中d?*/
var val = objectRef.testNumber;
/* 现在Q?- val - 中保存着刚赋l对象命名属性的?8*/

而且Q由于所有对象都有原型,而原型本w也是对象,所以原型也可能有原型,q样构成了所谓的原型链。原型链l止于链中原型ؓ null 的对象?code>Object 构造函数的默认原型有一?null 原型Q因此:

var objectRef = new Object(); //创徏一个普通的 JavaScript 对象?/span>

创徏了一个原型ؓ Object.prototype 的对象,而该原型自n则拥有一个gؓ null 的原型。也是_objectRef 的原型链中只包含一个对象-Q?Object.prototype。但对于下面的代码而言Q?

/* 创徏 - MyObject1 - cd对象的函?/
function MyObject1(formalParameter){
/* l创建的对象d一个名?- testNumber -
的属性ƈ传递给构造函数的W一个参数指定ؓ该属性的|*/
this.testNumber = formalParameter;
}
/* 创徏 - MyObject2 - cd对象的函?/
function MyObject2(formalParameter){
/* l创建的对象d一个名?- testString -
的属性ƈ传递给构造函数的W一个参数指定ؓ该属性的|*/
this.testString = formalParameter;
}

/* 接下来的操作?MyObject1 cȝ实例替换了所有与 MyObject2
cȝ实例相关联的原型。而且Qؓ MyObject1 构造函C递了参数
- 8 - Q因而其 - testNumber - 属性被赋予该|*/
MyObject2.prototype = new MyObject1( 8 );

/* 最后,一个字W串作ؓ构造函数的W一个参敎ͼ
创徏一?- MyObject2 - 的实例,q将指向该对象的
引用赋给变量 - objectRef - Q?/
var objectRef = new MyObject2( "String_Value" );

被变?objectRef 所引用?MyObject2 的实例拥有一个原型链。该链中的第一个对象是在创建后被指定给 MyObject2 构造函数的 prototype 属性的 MyObject1 的一个实例?code>MyObject1 的实例也有一个原型,即与 Object.prototype 所引用的对象对应的默认?Object 对象的原型。最后, Object.prototype 有一个gؓ null 的原型,因此q条原型铑ֈ此结束?

当某个属性访问器试d?objectRef 所引用的对象的属性值时Q整个原型链都会被搜索。在下面q种单的情况下:

var val = objectRef.testString;

因ؓ objectRef 所引用?MyObject2 的实例有一个名?#8220;testString”的属性,因此被设|ؓ“String_Value”的该属性的D赋给了变?val。但是:

var val = objectRef.testNumber;

则不能从 MyObject2 实例自n中读取到相应的命名属性|因ؓ该实例没有这个属性。然而,变量 val 的g然被讄?8Q而不是未定义Q-q是因ؓ在该实例中查扄应的命名属性失败后Q解释程序会l箋查其原型对象。而该实例的原型对象是 MyObject1 的实例,q个实例有一个名?#8220;testNumber”的属性ƈ且gؓ 8Q所以这个属性访问器最后会取得?8。而且Q虽?MyObject1 ?MyObject2 都没有定?toString ҎQ但是当属性访问器通过 objectRef d toString 属性的值时Q?

var val = objectRef.toString;

变量 val 也会被赋予一个函数的引用。这个函数就是在 Object.prototype ?toString 属性中所保存的函数。之所以会q回q个函数Q是因ؓ发生了搜?code>objectRef 原型铄q程。当在作为对象的 objectRef 中发现没?#8220;toString”属性存在时Q会搜烦其原型对象,而当原型对象中不存在该属性时Q则会l搜索原型的原型。而原型链中最l的原型?Object.prototypeQ这个对象确实有一?toString ҎQ因此该Ҏ的引用被q回?

最后:

var val = objectRef.madeUpProperty;

q回 undefinedQ因为在搜烦原型铄q程中,直至 Object.prototype 的原型-QnullQ都没有扑ֈM对象有名?#8220;madeUpPeoperty”的属性,因此最l返?undefined?

不论是在对象或对象的原型中,d命名属性值的时候只q回首先扑ֈ的属性倹{而当为对象的命名属性赋值时Q如果对象自w不存在该属性则创徏相应的属性?

q意味着Q如果执行像 objectRef.testNumber = 3 q样一条赋D句,那么q个 MyObject2 的实例自w也会创Z个名?#8220;testNumber”的属性,而之后Q何读取该命名属性的试都将获得相同的新倹{这时候,属性访问器不会再进一步搜索原型链Q但 MyObject1 实例gؓ 8 ?#8220;testNumber”属性ƈ没有被修攏V给 objectRef 对象的赋值只是遮挡了其原型链中相应的属性?

注意QECMAScript ?Object cd定义了一个内?[[prototype]] 属性。这个属性不能通过脚本直接讉KQ但在属性访问器解析q程中,则需要用到这个内?[[prototype]] 属性所引用的对象链Q-卛_型链。可以通过一个公q prototype 属性,来对与内部的 [[prototype]] 属性对应的原型对象q行赋值或定义。这两者之间的关系?ECMA 262Q?rd editionQ中有详l描qͼ但超Z本文要讨论的范畴?

标识W解析、执行环境和作用域链

执行环境

执行环境?ECMAScript 规范QECMA 262 W?3 版)用于定义 ECMAScript 实现必要行ؓ的一个抽象的概念。对如何实现执行环境Q规范没有作规定。但׃执行环境中包含引用规范所定义l构的相兛_性,因此执行环境中应该保有(甚至实现Q带有属性的对象Q-即属性不是公共属性?

所?JavaScript 代码都是在一个执行环境中被执行的。全局代码Q作为内|的 JS 文g执行的代码,或?HTML 面加蝲的代码)是在我将UC?#8220;全局执行环境”的执行环境中执行的,而对函数的每ơ调用(有可能是作ؓ构造函敎ͼ同样有关联的执行环境。通过 eval 函数执行的代码也有截然不同的执行环境Q但因ؓ JavaScript E序员在正常情况下一般不会?evalQ所以这里不作讨论。有x行环境的详细说明请参?ECMA 262Q第 3 版)W?10.2 节?

当调用一?JavaScript 函数Ӟ该函数就会进入相应的执行环境。如果又调用了另外一个函敎ͼ或者递归地调用同一个函敎ͼQ则又会创徏一个新的执行环境,q且在函数调用期间执行过E都处于该环境中。当调用的函数返回后Q执行过E会q回原始执行环境。因而,q行中的 JavaScript 代码构成了一个执行环境栈?

在创建执行环境的q程中,会按照定义的先后序完成一pd操作。首先,在一个函数的执行环境中,会创Z?#8220;zd”对象。活动对象是规范中规定的另外一U机制。之所以称之ؓ对象Q是因ؓ它拥有可讉K的命名属性,但是它又不像正常对象那样h原型Q至没有预定义的原型)Q而且不能通过 JavaScript 代码直接引用zd对象?

为函数调用创建执行环境的下一步是创徏一?arguments 对象Q这是一个类似数l的对象Q它以整数烦引的数组成员一一对应C存着调用函数时所传递的参数。这个对象也?length ?callee 属性(q两个属性与我们讨论的内Ҏ养I详见规范Q。然后,会ؓzd对象创徏一个名?#8220;arguments”的属性,该属性引用前面创建的 arguments 对象?

接着Qؓ执行环境分配作用域。作用域由对象列表(链)l成。每个函数对象都有一个内部的 [[scope]] 属性(该属性我们稍后会详细介绍Q,q个属性也由对象列表(链)l成。指定给一个函数调用执行环境的作用域,p函数对象?[[scope]] 属性所引用的对象列表(链)l成Q同Ӟzd对象被添加到该对象列表的剙Q链的前端)?

之后会发生由 ECMA 262 中所?#8220;可变”对象完成?#8220;变量实例?#8221;的过E。只不过此时使用zd对象作ؓ可变对象Q这里很重要Q请注意Q它们是同一个对象)。此时会函数的形式参数创徏为可变对象命名属性,如果调用函数时传递的参数与Ş式参C_则将相应参数的Dl这些命名属性(否则Q会l命名属性赋 undefined |。对于定义的内部函数Q会以其声明时所用名UCؓ可变对象创徏同名属性,而相应的内部函数则被创徏为函数对象ƈ指定l该属性。变量实例化的最后一步是在函数内部声明的所有局部变量创Zؓ可变对象的命名属性?

Ҏ声明的局部变量创建的可变对象的属性在变量实例化过E会被赋?undefined 倹{在执行函数体内的代码、ƈ计算相应的赋D辑ּ之前不会对局部变量执行真正的实例化?

事实上,拥有 arguments 属性的zd对象和拥有与函数局部变量对应的命名属性的可变对象是同一个对象。因此,可以标识符 arguments 作ؓ函数的局部变量来看待?

回到剙

最后,在this可以被用之前,q必d对其赋倹{如果赋的值是一个对象的引用Q则 this.m 讉K的便是该对象上的 m。如果(内部Q赋的值是 nullQ则this指向全局对象?Q此D는 pangba 刘未?/a> 译Q?

Q原文备考:Finally a value is assigned for use with the this keyword. If the value assigned refers to an object then property accessors prefixed with the this keyword reference properties of that object. If the value assigned (internally) is null then the this keyword will refer to the global object. Q?

创徏全局执行环境的过E会E有不同Q因为它没有参数Q所以不需要通过定义的活动对象来引用q些参数。但全局执行环境也需要一个作用域Q而它的作用域铑֮际上只由一个对象-Q全局对象Q-l成。全局执行环境也会有变量实例化的过E,它的内部函数是涉及大部?JavaScript 代码的、常规的函数声明。而且Q在变量实例化过E中全局对象是可变对象Q这是Z么全局性声明的函数是全局对象属性的原因。全局性声明的变量同样如此?

全局执行环境也会使用 this 对象来引用全局对象?

作用域链?[[scope]]

调用函数时创建的执行环境会包含一个作用域链,q个作用域链是通过该执行环境的活动(可变Q对象添加到保存于所调用函数对象?[[scope]] 属性中的作用域铑։端而构成的。所以,理解函数对象内部?[[scope]] 属性的定义q程臛_重要?

?ECMAScript 中,函数也是对象。函数对象在变量实例化过E中会根据函数声明来创徏Q或者是在计函数表辑ּ或调?Function 构造函数时创徏?

通过调用 Function 构造函数创建的函数对象Q其内部?[[scope]] 属性引用的作用域链中始l只包含全局对象?

通过函数声明或函数表辑ּ创徏的函数对象,其内部的 [[scope]] 属性引用的则是创徏它们的执行环境的作用域链?

在最单的情况下,比如声明如下全局函数Q?

function exampleFunction(formalParameter){
...   // 函数体内的代?/span>
}

当ؓ创徏全局执行环境而进行变量实例化Ӟ会根据上面的函数声明创徏相应的函数对象。因为全局执行环境的作用域链中只包含全局对象Q所以它q自己创徏的、ƈ以名?#8220;exampleFunction”的属性引用的q个函数对象的内?[[scope]] 属性,赋予了只包含全局对象的作用域链?

当在全局环境中计函数表辑ּӞ也会发生cM的指定作用域铄q程Q?

var exampleFuncRef = function(){
...   // 函数体代?/span>
}

在这U情况下Q不同的是在全局执行环境的变量实例化q程中,会先为全局对象创徏一个命名属性。而在计算赋D句之前,暂时不会创徏函数对象Q也不会该函数对象的引用指定给全局对象的命名属性。但是,最l还是会在全局执行环境中创个函数对象(当计函数表辑ּ时。译者注Q,而ؓq个创徏的函数对象的 [[scope]] 属性指定的作用域链中仍然只包含全局对象?

内部的函数声明或表达式会D在包含它们的外部函数的执行环境中创徏相应的函数对象,因此q些函数对象的作用域链会E微复杂一些。在下面的代码中Q先定义了一个带有内部函数声明的外部函数Q然后调用外部函敎ͼ

function exampleOuterFunction(formalParameter){
function exampleInnerFuncitonDec(){
... // 内部函数体代?/span>
}
...  // 其余的外部函C代码
}
exampleOuterFunction( 5 );

与外部函数声明对应的函数对象会在全局执行环境的变量实例化q程中被创徏。因此,外部函数对象?[[scope]] 属性中会包含一个只有全局对象?#8220;单项?#8221;作用域链?

当在全局执行环境中调?exampleOuterFunction 函数Ӟ会ؓ该函数调用创Z个新的执行环境和一个活动(可变Q对象。这个新执行环境的作用域q新的zd对象后跟外部函数对象?[[scope]] 属性所引用的作用域链(只有全局对象Q构成。在新执行环境的变量实例化过E中Q会创徏一个与内部函数声明对应的函数对象,而同时会l这个函数对象的 [[scope]] 属性指定创函数对象的执行环境(x执行环境。译者注Q的作用域|Q即一个包含活动对象后跟全局对象的作用域链?

到目前ؓ止,所有过E都是自动、或者由源代码的l构所控制的。但我们发现Q执行环境的作用域链定义了执行环境所创徏的函数对象的 [[scope]] 属性,而函数对象的 [[scope]] 属性则定义了它的执行环境的作用域(包括相应的活动对象)。不q,ECMAScript 也提供了用于修改作用域链 with 语句?

with 语句会计一个表辑ּQ如果该表达式是一个对象,那么将q个对象d到当前执行环境的作用域链中(在活?lt;可变>对象之前Q。然后,执行 with 语句Q它自n也可能是一个语句块Q中的其他语句。之后,又恢复到调用它之前的执行环境的作用域链中?

with 语句不会影响在变量实例化q程中根据函数声明创建函数对象。但是,可以在一?with 语句内部对函数表辑ּ求|-

/* 创徏全局变量 - y - 它引用一个对象:- */
var y = {x:5}; // 带有一个属?- x - 的对象直接量
function exampleFuncWith(){
var z;
/* 全局对象 - y - 引用的对象添加到作用域链的前端:- */
with(y){
/* 对函数表辑ּ求|以创建函数对象ƈ该函数对象的引用指定给局部变?- z - :-  */
z = function(){
... // 内部函数表达式中的代?
}
}
...
}
/* 执行 - exampleFuncWith - 函数:- */
exampleFuncWith();

在调?exampleFuncWith 函数所创徏的执行环境中包含一个由其活动对象后跟全局对象构成的作用域链。而在执行 with 语句Ӟ又会把全局变量 y 引用的对象添加到q个作用域链的前端。在对其中的函数表达式求值的q程中,所创徏函数对象?[[scope]] 属性与创徏它的执行环境的作用域保持一_Q即Q该属性会引用一个由对象 y 后跟调用外部函数时所创徏执行环境的活动对象,后跟全局对象的作用域链?

当与 with 语句相关的语句块执行l束Ӟ执行环境的作用域得以恢复Q?code>y 会被U除Q,但是已经创徏的函数对象(z。译者注Q的 [[scope]] 属性所引用的作用域链中位于最前面的仍然是对象 y?

标识W解?/a>

标识W是沿作用域N向解析的。ECMA 262 ?this 归类为关键字而不是标识符Qƈ非不合理。因?this 值时始终要根据用它的执行环境来判断Q而与作用域链无关?

标识W解析从作用域链中的W一个对象开始。检查该对象中是否包含与标识W对应的属性名。因Z用域链是一条对象链Q所以这个检查过E也会包含相应对象的原型链(如果有)。如果没有在作用域链的第一个对象中发现相应的|解析q程会l搜索下一个对象。这样依ơ类推直xC用域链中包含以标识符为属性名的对象ؓ止,也有可能在作用域铄所有对象中都没有发现该标识W?

当基于对象用属性访问器Ӟ也会发生与上面相同的标识W解析过E。当属性访问器中有相应的属性可以替换某个对象时Q这个属性就成ؓ表示该对象的标识W,该对象在作用域链中的位置q而被定。全局对象始终都位于作用域铄?

因ؓ与函数调用相关的执行环境会把活动(可变Q对象添加到作用域链的前端,所以在函数体内使用的标识符会首先检查自己是否与形式参数、内部函数声明的名称或局部变量一致。这些都可以由活动(可变Q对象的命名属性来定?

闭包

自动垃圾攉

ECMAScript 要求使用自动垃圾攉机制。但规范中ƈ没有详细说明相关的细节,而是留给了实现来军_。但据了解,相当一部分实现对它们的垃圾攉操作只赋予了很低的优先。但是,大致的思想都是相同的,卛_果对象不?#8220;可引用(׃不存在对它的引用Q执行代码无法再访问到它)”Ӟ该对象就成ؓ垃圾攉的目标。因而,在将来的某个时刻会将q个对象销毁ƈ它所占用的一切资源释放,以便操作pȝ重新利用?

正常情况下,当退Z个执行环境时׃满cM的条件。此Ӟ作用域链l构中的zdQ可变)对象以及在该执行环境中创建的M对象Q-包括函数对象Q都不再“可引?#8221;Q因此将成ؓ垃圾攉的目标?

构成闭包

闭包是通过在对一个函数调用的执行环境中返回一个函数对象构成的。比如,在对函数调用的过E中Q将一个对内部函数对象的引用指定给另一个对象的属性。或者,直接这样一个(内部Q函数对象的引用指定l一个全局变量、或者一个全局性对象的属性,或者一个作为参C引用方式传递给外部函数的对象。例如:-

function exampleClosureForm(arg1, arg2){
var localVar = 8;
function exampleReturned(innerArg){
return ((arg1 + arg2)/(innerArg + localVar));
}
/* q回一个定义ؓ exampleReturned 的内部函数的引用 -:-  */
return exampleReturned;
}
var globalVar = exampleClosureForm(2, 4);

q种情况下,在调用外部函?exampleClosureForm 的执行环境中所创徏的函数对象就不会被当作垃圾收集,因ؓ该函数对象被一个全局变量所引用Q而且仍然是可以访问的Q甚臛_以通过 globalVar(n) 来执行?

的确Q情冉|正常的时候要复杂一些。因为现在这个被变量 globalVar 引用的内部函数对象的 [[scope]] 属性所引用的作用域链中Q包含着属于创徏该内部函数对象的执行环境的活动对象(和全局对象Q。由于在执行?globalVar 引用的函数对象时Q每ơ都要把该函数对象的 [[scope]] 属性所引用的整个作用域链添加到创徏的(内部函数的)执行环境的作用域中(x时的作用域中包括Q内部执行环境的zd对象、外部执行环境的zd对象、全局对象。译者注Q, 所以这个(外部执行环境的)zd对象不会被当作垃圾收集?

闭包因此而构成。此Ӟ内部函数对象拥有自由的变量,而位于该函数作用域链中的zdQ可变)对象则成Z变量l定的环境?

׃zdQ可变)对象受限于内部函数对象(现在?globalVar 变量引用Q的 [[scope]] 属性中作用域链的引用,所以活动对象连同它的变量声明-Q即属性的|都会被保留。而在对内部函数调用的执行环境中进行作用域解析Ӟ会把与zdQ可变)对象的命名属性一致的标识W作对象的属性来解析。活动对象的q些属性值即使是在创建它的执行环境退出后Q仍然可以被d和设|?

在上面的例子中,当外部函数返回(退出它的执行环境)Ӟ其活动(可变Q对象的变量声明中记录了形式参数、内部函数定义以及局部变量的倹{?code>arg1 属性的gؓ 2Q?arg2 属性的gؓ 4Q?code>localVar 的值是 8Q还有一?exampleReturned 属性,它引用由外部函数q回的内部函数对象。(为方便v见,我们在后面的讨ZQ称q个zd<可变>对象?"ActOuter1"Q?

如果再次调用 exampleClosureForm 函数Q如Q?

var secondGlobalVar = exampleClosureForm(12, 3);

- 则会创徏一个新的执行环境和一个新的活动对象。而且Q会q回一个新的函数对象,该函数对象的 [[scope]] 属性引用的作用域链与前一ơ不同,因ؓq一ơ的作用域链中包含着W二个执行环境的zd对象Q而这个活动对象的属?arg112 而属?arg23。(为方便v见,我们在后面的讨ZQ称q个zd<可变>对象?"ActOuter2"Q?

通过W二ơ执?exampleClosureForm 函数Q第二个、也是截然不同的闭包诞生了?

通过执行 exampleClosureForm 创徏的两个函数对象分别被指定l了全局变量 globalVar ?secondGlobalVarQƈq回了表辑ּ ((arg1 + arg2)/(innerArg + localVar))。该表达式对其中的四个标识符应用了不同的操作W。如何确定这些标识符的值是体现闭包价值的关键所在?

我们来看一看,在执行由 globalVar 引用的函数对象-Q如 globalVar(2)Q-时的情Ş。此Ӟ会创Z个新的执行环境和相应的活动对象(我们称之ؓ“ActInner1”Q,q把该活动对象添加到执行的函数对象的 [[scope]] 属性所引用的作用域铄前端。ActInner1 会带有一个属?innerArgQ根据传递的形式参数Q其D指定?2。这个新执行环境的作用域铑֏成: ActInner1->ActOuter1->全局对象.

Zq回表达?((arg1 + arg2)/(innerArg + localVar)) 的|要沿着作用域链q行标识W解析。表辑ּ中标识符的值将通过依次查找作用域链中每个对象(与标识符名称一_的属性来定?

作用域链中的W一个对象是 ActInner1Q它有一个名?innerArg 的属性,值是 2。所有其他三个标识符?ActOuter1 中都有对应的属性:arg1 ?2Q?code>arg2 ?4 ?localVar ?8。最后,函数调用q回 ((2 + 2)/(2 + 8))?

现在再来看一看由 secondGlobalVar 引用的同一个函数对象的执行情况Q比?secondGlobalVar(5)。我们把q次创徏的新执行环境的活动对象称?“ActInner2”Q相应的作用域链变成了Q?span class="scopeCh">ActInner2->ActOuter2->全局对象。ActInner2 q回 innerArg 的?5Q?ActOuter2 分别q回 arg1?code>arg2 ?localVar 的?12?code>3 ?8。函数调用返回的值就?((12 + 3)/(5 + 8))?

如果再执行一?secondGlobalVarQ则又会有一个新zd对象被添加到作用域链的前端,?ActOuter2 仍然是链中的W二个对象,而他的命名属性会再次用于完成标识W?arg1?code>arg2 ?localVar 的解析?

q就?ECMAScript 的内部函数获取、维持和讉K创徏他们的执行环境的形式参数、声明的内部函数以及局部变量的q程。这个过E说明了构成闭包以后Q内部的函数对象在其存箋q程中,如何l持对这些值的引用、如何对q些D行读取的机制。即Q创建内部函数对象的执行环境的活动(可变Q对象,会保留在该函数对象的 [[scope]] 属性所引用的作用域链中。直到所有对q个内部函数的引用被释放Q这个函数对象才会成为垃圾收集的目标Q连同它的作用域链中M不再需要的对象Q?

内部函数自n也可能有内部函数。在通过函数执行q回内部函数构成闭包以后Q相应的闭包自n也可能会q回内部函数从而构成它们自q闭包。每ơ作用域铑ֵ套,都会增加由创建内部函数对象的执行环境引发的新zd对象。ECMAScript 规范要求作用域链是时性的Q但对作用域铄长度却没有加以限制。在具体实现中,可能会存在实际的限制Q但q没有发现有具体限制数量的报告。目前来看,嵌套的内部函数所拥有的潜能,仍然出了用它们的人的惛_能力?

通过闭包可以做什么?

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

?1Qؓ函数引用讄延时

闭包的一个常见用法是在执行函C前ؓ要执行的函数提供参数。例如:函C?setTimout 函数的第一个参敎ͼq在 Web 览器的环境下是非常常见的一U应用?

setTimeout 用于有计划地执行一个函敎ͼ或者一?JavaScript 代码Q不是在本例中)Q要执行的函数是其第一个参敎ͼ其第二个参数是以毫秒表示的执行间隔。也是_当在一D代码中使用 setTimeout Ӟ要将一个函数的引用作ؓ它的W一个参敎ͼ而将以毫U表C的旉g为第二个参数。但是,传递函数引用的同时无法划执行的函数提供参数?

然而,可以在代码中调用另外一个函敎ͼ由它q回一个对内部函数的引用,再把q个对内部函数对象的引用传递给 setTimeout 函数。执行这个内部函数时要用的参数在调用返回它的外部函数时传递。这PsetTimeout 在执行这个内部函数时Q不用传递参敎ͼ但该内部函数仍然能够讉K在调用返回它的外部函数时传递的参数Q?

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

return (function(){
/* q个内部函数通过 - setTimeout - 执行Q?
而且当它执行时它会读取ƈ按照传递给
外部函数的参数行事:
*/
paramA[paramB] = paramC;
});
}
...
/* 调用q个函数返回一个在其执行环境中创徏的内部函数对象的引用?
传递的参数最l将作ؓ外部函数的参数被内部函数使用?
q回的对内部函数的引用被赋给一个全局变量:-
*/

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

hideMenu=setTimeout(functRef, 500);

?2: 通过对象实例Ҏ兌函数

回到剙

许多时候我们需要将一个函数对象暂时挂C个引用上留待后面执行Q因Z{到执行的时候是很难知道其具体参数的Q而先前将它赋l那个引用的时候更是压根不知道的?Q此D는 pangba 刘未?/a> 译Q?

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

Q原文备考: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.Q?/p>

一个相关的例子是,?JavaScript 对象来封装与特定 DOM 元素的交互。这?JavaScript 对象h doOnClick?code>doMouseOver ?doMouseOut ҎQƈ且当用户在该特定?DOM 元素中触发了相应的事件时要执行这些方法。不q,可能会创Z不同?DOM 元素兌的Q意数量的 JavaScript 对象Q而且每个对象实例q不知道实例化它们的代码会如何操纵它们Q即注册事g处理函数与定义相应的事g处理函数分离。译者注Q。这些对象实例ƈ不知道如何在全局环境中引用它们自w,因ؓ它们不知道将会指定哪个全局变量Q如果有Q引用它们的实例?

因而问题可以归lؓ执行一个与特定?JavaScript 对象兌的事件处理函敎ͼq且要知道调用该对象的哪个方法?/p>

下面q个例子使用了一个基于闭包构建的一般化的函敎ͼ此句多谢未鹏指点Q,该函C对象实例与 DOM 元素事g兌hQ安排执行事件处理程序时调用对象实例的指定方法,l象的指定方法传递的参数是事件对象和与元素关联的引用Q该函数q回执行相应Ҏ后的q回倹{?

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

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

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

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

return obj[methodName](e, this);
});
}
/* q个构造函数用于创建将自n?DOM 元素兌的对象,
DOM 元素?ID 作ؓ构造函数的字符串参数?
所创徏的对象会在相应的元素触发 onclick?
onmouseover ?onmouseout 事gӞ
调用相应的方法?
*/

function DhtmlObject(elementId){
/* 调用一个返?DOM 元素Q如果没扑ֈq回 nullQ引用的函数Q?
必需的参数是 ID?返回的Dl局部变?- el -?
*/ 
var el = getElementWithId(elementId);
 /* - el - g在内部通过cd转换变ؓ布尔|以便 - if - 语句加以判断?
因此Q如果它引用一个对象结果将q回 trueQ如果是 null 则返?false?
下面的代码块只有?- el - 变量q回一?DOM 元素时才会被执行?
*/
if(el){
/* 为给元素的事件处理器指定一个函敎ͼ该对象调用了
- associateObjWithEvent - 函数?
同时对象自w(通过 - this - 关键字)作ؓ调用Ҏ的对象,
q提供了调用的方法名U?- associateObjWithEvent - 函数会返?
一个内部函敎ͼ该内部函数被指定?DOM 元素的事件处理器?
在响应事件时Q执行这个内部函数就会调用必要的Ҏ?
*/
el.onclick = associateObjWithEvent(this, "doOnClick");
el.onmouseover = associateObjWithEvent(this, "doMouseOver");
el.onmouseout = associateObjWithEvent(this, "doMouseOut");
...
}
}
DhtmlObject.prototype.doOnClick = function(event, element){
... // doOnClick Ҏ体?/span>.
}
DhtmlObject.prototype.doMouseOver = function(event, element){
... // doMouseOver Ҏ体?/span>
}
DhtmlObject.prototype.doMouseOut = function(event, element){
... // doMouseOut Ҏ体?/span>
}

q样Q?code>DhtmlObject 的Q何实例都会将自n与相应的 DOM 元素兌hQ而这?DOM 元素不必知道其他代码如何操纵它们Q即当触发相应事件时Q会执行什么代码。译者注Q,也不必理会全局命名I间的媄响以及与 DhtmlObject 的其他实例间存在冲突的危险?

?3Q包装相关的功能

闭包可以用于创徏额外的作用域Q通过该作用域可以相关的和具有依赖性的代码l织hQ以便将意外交互的风险降到最低。假设有一个用于构建字W串的函敎ͼZ避免重复性的q接操作Q和创徏众多的中间字W串Q,我们的愿望是使用一个数l按序来存储字W串的各个部分,然后再?Array.prototype.join ҎQ以I字W串作ؓ其参敎ͼ输出l果。这个数l将作ؓ输出的缓冲器Q但是将数组作ؓ函数的局部变量又会导致在每次调用函数旉重新创徏一个新数组Q这在每ơ调用函数时只重新指定数l中的可变内容的情况下ƈ不是必要的?

一U解x案是这个数l声明ؓ全局变量Q这样就可以重用q个数组Q而不必每ơ都建立新数l。但q个Ҏ的结果是Q除了引用函数的全局变量会用这个缓冲数l外Q还会多Z个全局属性引用数l自w。如此不仅代码变得不容易管理,而且Q如果要在其他地方用这个数l时Q开发者必要再次定义函数和数l。这样一来,也得代码不Ҏ与其他代码整合,因ؓ此时不仅要保证所使用的函数名在全局命名I间中是唯一的,而且q要保证函数所依赖的数l在全局命名I间中也必须是唯一的?

而通过闭包可以使作为缓冲器的数l与依赖它的函数兌hQ优雅地打包Q,同时也能够维持在全局命名I间外指定的~冲数组的属性名Q免除了名称冲突和意外交互的危险?

其中的关键技巧在于通过执行一个单行(in-lineQ函数表辑ּ创徏一个额外的执行环境Q而将该函数表辑ּq回的内部函C为在外部代码中用的函数。此Ӟ~冲数组被定义ؓ函数表达式的一个局部变量。这个函数表辑ּ只需执行一ơ,而数l也只需创徏一ơ,可以供依赖它的函数重复使用?

下面的代码定义了一个函敎ͼq个函数用于q回一?HTML 字符Ԍ其中大部分内定w是常量,但这些常量字W序列中需要穿插一些可变的信息Q而可变的信息p用函数时传递的参数提供?

通过执行单行函数表达式返回一个内部函敎ͼq将q回的函数赋l一个全局变量Q因此这个函C可以UCؓ全局函数。而缓冲数l被定义为外部函数表辑ּ的一个局部变量。它不会暴露在全局命名I间中,而且无论什么时候调用依赖它的函数都不需要重新创个数l?

/* 声明一个全局变量 - getImgInPositionedDivHtml -
q将一ơ调用一个外部函数表辑ּq回的内部函数赋l它?
q个内部函数会返回一个用于表C绝对定位的 DIV 元素
包围着一?IMG 元素 ?HTML 字符Ԍq样一来,
所有可变的属性值都p用该函数时的参数提供Q?
*/
var getImgInPositionedDivHtml = (function(){
/* 外部函数表达式的局部变?- buffAr - 保存着~冲数组?
q个数组只会被创Zơ,生成的数l实例对内部函数而言永远是可用的
因此Q可供每ơ调用这个内部函数时使用?
其中的空字符串用作数据占位符Q相应的数据
由内部函数插入到这个数l中Q?
*/
var buffAr = [
'<div id="',
'',   //index 1, DIV ID 属?/span>
'" style="position:absolute;top:',
'',   //index 3, DIV 剙位置
'px;left:',
'',   //index 5, DIV 左端位置
'px;width:',
'',   //index 7, DIV 宽度
'px;height:',
'',   //index 9, DIV 高度
'px;overflow:hidden;\"><img src=\"',
'',   //index 11, IMG URL
'\" width=\"',
'',   //index 13, IMG 宽度
'\" height=\"',
'',   //index 15, IMG 调蓄
'\" alt=\"',
'',   //index 17, IMG alt 文本内容
'\"><\/div>'
];
/* q回作ؓ对函数表辑ּ求值后l果的内部函数对象?
q个内部函数是每次调用执行的函?
- getImgInPositionedDivHtml( ... ) -
*/
return (function(url, id, width, height, top, left, altText){
/* 不同的参数插入到缓冲数l相应的位置Q?
*/
buffAr[1] = id;
buffAr[3] = top;
buffAr[5] = left;
buffAr[13] = (buffAr[7] = width);
buffAr[15] = (buffAr[9] = height);
buffAr[11] = url;
buffAr[17] = altText;
/* q回通过使用I字W串Q相当于数l元素连接v来)
q接数组每个元素后Ş成的字符Ԍ
*/
return buffAr.join('');
}); //:内部函数表达式结束?/span>
})();
/*^^- :单行外部函数表达式?/

如果一个函C赖于另一Q或多)个其他函敎ͼ而其他函数又没有必要被其他代码直接调用,那么可以q用相同的技术来包装q些函数Q而通过一个公开暴露的函数来调用它们。这P将一个复杂的多函数处理过E封装成了一个具有移植性的代码单元?

其他例子

有关闭包的一个可能是最qؓ人知的应用是 Douglas Crockford's technique for the emulation of private instance variables in ECMAScript objects。这U应用方式可以扩展到各种嵌套包含的可讉K性(或可见性)的作用域l构Q包?the emulation of private static members for ECMAScript objects?

闭包可能的用途是无限的,可能理解其工作原理才是把握如何用它的最好指南?

意外的闭?/a>

在创建可讉K的内部函数的函数体之外解析该内部函数׃构成闭包。这表明闭包很容易创建,但这样一来可能会D一U结果,x有认识到闭包是一U语aҎ的 JavaScript 作者,会按照内部函数能完成多种d的想法来使用内部函数。但他们对用内部函数的l果q不明了Q而且Ҏ意识不到创徏了闭包,或者那样做意味着什么?

正如下一节谈?IE 中内存泄漏问题时所提及的,意外创徏的闭包可能导致严重的负面效应Q而且也会影响C码的性能。问题不在于闭包本nQ如果能够真正做到}慎地使用它们Q反而会有助于创建高效的代码。换句话_使用内部函数会媄响到效率?

使用内部函数最常见的一U情况就是将其作?DOM 元素的事件处理器。例如,下面的代码用于向一个链接元素添?onclick 事g处理器:

/* 定义一个全局变量Q通过下面的函数将它的?
作ؓ查询字符串的一部分d到链接的 - href - 中:
*/
var quantaty = 5;
/* 当给q个函数传递一个链接(作ؓ函数中的参数 - linkRef -Q时Q?
会将一?onclick 事g处理器指定给该链接,该事件处理器
全局变量 - quantaty - 的g为字W串d到链接的 - href -
属性中Q然后返?true 使该链接在单d定位到由  - href -
属性包含的查询字符串指定的资源Q?
*/
function addGlobalQueryOnClick(linkRef){
/* 如果可以参?- linkRef - 通过cd转换?ture
Q说明它引用了一个对象)Q?
*/
if(linkRef){
/* 对一个函数表辑ּ求|q将对该函数对象的引?
指定l这个链接元素的 onclick 事g处理器:
*/
linkRef.onclick = function(){
/* q个内部函数表达式将查询字符?
d到附加事件处理器的元素的 - href - 属性中Q?
*/
this.href += ('?quantaty='+escape(quantaty));
return true;
};
}
}

无论什么时候调?addGlobalQueryOnClick 函数Q都会创Z个新的内部函敎ͼ通过赋值构成了闭包Q。从效率的角度上看,如果只是调用一两次 addGlobalQueryOnClick 函数q没有什么大的妨,但如果频J用该函数Q就会导致创多截然不同的函数对象Q每对内部函数表辑ּ求一ơ|׃产生一个新的函数对象)?

上面例子中的代码没有x内部函数在创建它的函数外部可以访问(或者说构成了闭包)q一事实。实际上Q同L效果可以通过另一U方式来完成。即单独地定义一个用于事件处理器的函敎ͼ然后该函数的引用指定给元素的事件处理属性。这P只需创徏一个函数对象,而所有用相同事件处理器的元素都可以׃n对这个函数的引用Q?

/* 定义一个全局变量Q通过下面的函数将它的?
作ؓ查询字符串的一部分d到链接的 - href - 中:
*/
var quantaty = 5;
/* 当把一个链接(作ؓ函数中的参数 - linkRef -Q传递给q个函数Ӟ
会给q个链接d一?onclick 事g处理器,该事件处理器?
全局变量  - quantaty - 的g为查询字W串的一部分d?
链接?- href -  中,然后q回 trueQ以便单击链接时定位到由
作ؓ - href - 属性值的查询字符串所指定的资源:
*/
function addGlobalQueryOnClick(linkRef){
/* 如果 - linkRef - 参数能够通过cd转换?true
Q说明它引用了一个对象)Q?
*/
if(linkRef){
/* 一个对全局函数的引用指定给q个链接
的事件处理属性,使函数成为链接元素的事g处理器:
*/
linkRef.onclick = forAddQueryOnClick;
}
}
/* 声明一个全局函数Q作为链接元素的事g处理器,
q个函数一个全局变量的gd事g处理器的
链接元素? - href - 值的一部分Q?
*/
function forAddQueryOnClick(){
this.href += ('?quantaty='+escape(quantaty));
return true;
}

在上面例子的W一个版本中Q内部函数ƈ没有作ؓ闭包发挥应有的作用。在那种情况下,反而是不用闭包更有效率,因ؓ不用重复创徏许多本质上相同的函数对象?

cM地考量同样适用于对象的构造函数。与下面代码中的构造函数框架类似的代码q不|见Q?

function ExampleConst(param){
/* 通过对函数表辑ּ求值创建对象的ҎQ?
q将求值所得的函数对象的引用赋l要创徏对象的属性:
*/
this.method1 = function(){
... // Ҏ体?/span>
};
this.method2 = function(){
... // Ҏ体?/span>
};
this.method3 = function(){
... // Ҏ体?/span>
};
/* 把构造函数的参数赋给对象的一个属性:
*/
this.publicProp = param;
}

每当通过 new ExampleConst(n) 使用q个构造函数创Z个对象时Q都会创Zl新的、作为对象方法的函数对象。因此,创徏的对象实例越多,相应的函数对象也p多?

Douglas Crockford 提出的模?JavaScript 对象U有成员的技术,利用了对内部函数的引用指定给在构造函C构造对象的公共属性而Ş成的闭包。如果对象的Ҏ没有利用在构造函C形成的闭包,那么在实例化每个对象时创建的多个函数对象Q会使实例化q程变慢Q而且有更多的资源被占用Q以满创徏更多函数对象的需要?

q那U情况下Q只创徏一ơ函数对象,q把它们指定l构造函?prototype 的相应属性显然更有效率。这样一来,它们p被构造函数创建的所有对象共享了Q?

function ExampleConst(param){
/* 构造函数的参数赋给对象的一个属性:
*/
this.publicProp = param;
}
/* 通过对函数表辑ּ求|q将l果函数对象的引?
指定l构造函数原型的相应属性来创徏对象的方法:
*/
ExampleConst.prototype.method1 = function(){
... // Ҏ体?/span>
};
ExampleConst.prototype.method2 = function(){
... // Ҏ体?/span>
};
ExampleConst.prototype.method3 = function(){
... // Ҏ体?/span>
};

Internet Explorer 的内存泄漏问?

Internet Explorer Web 览器(?IE 4 ?IE 6 中核实)的垃圾收集系l中存在一个问题,卛_?ECMAScript 和某些宿d象构成了 "循环引用"Q那么这些对象将不会被当作垃圾收集。此时所谓的宿主对象指的是Q?DOM 节点Q包?document 对象及其后代元素Q和 ActiveX 对象。如果在一个@环引用中包含了一或多个这L对象Q那么这些对象直到浏览器关闭都不会被释放Q而它们所占用的内存同样在览器关闭之前都不会交回pȝ重用?

当两个或多个对象以首q的方式怺引用Ӟ构成了循环引用。比如对?1 的一个属性引用了对象 2 Q对?2 的一个属性引用了对象 3Q而对?3 的一个属性又引用了对?1。对于纯_的 ECMAScript 对象而言Q只要没有其他对象引用对?1??Q也是说它们只是相互之间的引用Q那么仍然会被垃圾收集系l识别ƈ处理。但是,?Internet Explorer 中,如果循环引用中的M对象?DOM 节点或?ActiveX 对象Q垃圾收集系l则不会发现它们之间的@环关pMpȝ中的其他对象是隔ȝq攑֮们。最l它们将被保留在内存中,直到览器关闭?

闭包非常Ҏ构成循环引用。如果一个构成闭包的函数对象被指定给Q比如一?DOM 节点的事件处理器Q而对该节点的引用又被指定l函数对象作用域中的一个活动(或可变)对象Q那么就存在一个@环引用?span class="scopeCh">DOM_Node.onevent ->function_object.[[scope]] ->scope_chain ->Activation_object.nodeRef ->DOM_Node。Ş成这样一个@环引用是轻而易丄Q而且E微览一下包含类似@环引用代码的|站Q通常会出现在|站的每个页面中Q,׃消耗大量(甚至全部Q系l内存?

多加注意可以避免形成循环引用Q而在无法避免Ӟ也可以用补偿的ҎQ比如?IE ?onunload 事g来来清空QnullQ事件处理函数的引用。时L识到q个问题q理解闭包的工作机制是在 IE 中避免此c问题的关键?

comp.lang.javascript FAQ notes T.O.C.

  • 撰稿 Richard CornfordQ?004 q?3 ?
  • 修改来自Q?br />
    • Martin Honnen.
    • Yann-Erwan Perio (Yep).
    • Lasse Reichstein Nielsen. (definition of closure)
    • Mike Scirocco.
    • Dr John Stockton.


xyz 2008-11-15 00:14 发表评论
]]>
DWR与Struts整合http://www.tkk7.com/xyzroundo/articles/228547.htmlxyzxyzFri, 12 Sep 2008 03:21:00 GMThttp://www.tkk7.com/xyzroundo/articles/228547.htmlhttp://www.tkk7.com/xyzroundo/comments/228547.htmlhttp://www.tkk7.com/xyzroundo/articles/228547.html#Feedback1http://www.tkk7.com/xyzroundo/comments/commentRss/228547.htmlhttp://www.tkk7.com/xyzroundo/services/trackbacks/228547.html 1.下載DWR Version 2
https://dwr.dev.java.net/files/documents/2427/47504/dwr.jar

2.安裝DWR,把dwr.jar攑ֈWEB-INF/lib?

web.xml中加入DWRServlet & ActionServlet
其中<load-on-startup>的部分要特別注意,ActionServlet要先初始?所以數字要比較?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  <servlet>    
<servlet-name>action</servlet-name>    
<servlet-class>org.apache.struts.action.ActionServlet
</servlet-class>    
<init-param>      
<param-name>config</param-name>      
<param-value>/WEB-INF/struts-config.xml</param-value>    
</init-param>    
<init-param>      
<param-name>debug</param-name>      
<param-value>2</param-value>    
</init-param>    
<load-on-startup>2</load-on-startup>  
</servlet>  
<servlet-mapping>    
<servlet-name>action</servlet-name>    
<url-pattern>*.do</url-pattern>  
</servlet-mapping>  
<servlet>    
<servlet-name>dwr-invoker</servlet-name>    
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>    
<init-param>      
<param-name>debug</param-name>      
<param-value>true</param-value>    
</init-param>    
<load-on-startup>10</load-on-startup>  
</servlet>  
<servlet-mapping>   
 <servlet-name>dwr-invoker</servlet-name>    
<url-pattern>/dwr/*</url-pattern>  
</servlet-mapping>



dwr.xml中加入struts的設?其中formBean的參數的value?會對應到struts-config.xml?lt;form-beans>的設?
1
2
3
4
5
6
7
8
  <dwr>  
<allow>    
<create creator="struts" javascript="testFrm">      
<param name="formBean" value="testActionForm"/>    
</create>  
</allow>  
</dwr>  


struts-config.xml
1
2
3
4
5
6
7
8
9
10
11
  <struts-config>  
<form-beans>    
<form-bean name="testActionForm" type="test.struts.testActionForm" />  
</form-beans>  
<action-mappings>    
<action name="testActionForm" path="/testAction" scope="session" type="test.struts.testAction" validate="false">      
<forward name="display" path="/display.jsp" />    
</action>  
</action-mappings>  
<message-resources parameter="ApplicationResources" />
</struts-config>


testActionForm.java,getDate()會透過dwr,取得珑֜最新的日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package test.struts; 
import org.apache.struts.action.*;
import java.util.*; 
public class testActionForm extends ActionForm {     
private String strDate;     
public void setStrDate(String strDate) {        
this.strDate = strDate;    
}     
public String getStrDate() {        
return strDate;    
}    
 //dwr    public String getDate() {        
Date date = new Date();        
return date.toString();   
 } 
}


testAction.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package test.struts; 
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.Action;
import org.apache.struts.action.*; 
public class testAction extends Action {    
	public ActionForward execute(ActionMapping mapping, ActionForm form,                                 
HttpServletRequest request,                                 
HttpServletResponse response) {         
testActionForm actionForm = (testActionForm) form;       
 System.out.println(actionForm.getStrDate());        
return mapping.findForward("display");    
}
}

date.jsp,在form的部?請用struts ?tag library,我把<html:text property="strDate" size="30" >Ҏ<input type="text" name="strDate">?無法正常的接受到?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<%@ page contentType="text/html; charset=Big5" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html><head>
<title>title</title>  
<script type='text/javascript' src='dwr/interface/testFrm.js'></script>  
<script type='text/javascript' src='dwr/engine.js'></script>  
<script type='text/javascript' src='dwr/util.js'></script>
</head>
<SCRIPT LANGUAGE="JavaScript" type=""
function refreshDate() {   
 testFrm.getDate(populateDate)
;} 
function populateDate(data){   
DWRUtil.setValue('strDate', data);
} 
</script> 
<body> 
<html:form action="testAction.do">
dateQ?lt;html:text property="strDate" size="30" ></html:text> 
<input type="button" onclick="refreshDate();" value="更新日期"/><br/>   
<html:submit>送出  </html:submit>
</html:form></body></html>


display.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@ page contentType="text/html; charset=Big5" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
%@page import="test.struts.*"%
<html>
<head>
<title>test</title>
</head><body bgcolor="#ffffff"><h1>您送出的日?<br>
<bean:write name="testActionForm" property="strDate"/></h1>
</body>
</html>
 


xyz 2008-09-12 11:21 发表评论
]]>
վ֩ģ壺 h⶯߹ۿ| ëƬѹۿƵ| ޾Ʒtvþþþ| Ʒɫͼ| ĻӰѹۿ| AVһɫ| AëƬѹۿAVվ| | ëƬƵ| ŮҺƵ| AVһ߹ۿ| ŮƵѿһ| ޹˾þһҳ | ĻسƷ޸| 뿴һëƬѵ| ˸һþ| ޹˾þһҳ| йһëƬƵѿ| AVþþƷɫ| ֻˬֳƵ| ޾Һ| ͵ۺ| պѵƵ߹ۿ㽶| ŷձƷ| | Ʒ߹ۿѲ| ޾ƷƵ| Ƶѹۿ| 弶18ѿ| ˳վ߲ӰԺ| רAVվ| ްv2017| ޵һҳƵ| ۺվ222| Ļѿ| ҹƷӰ߹ۿ| ѹۿAVƬ| 99þĻƷ| Ʒ޳a߹ۿ| ޹ƷƬ߹ۿ| Ƭѿڵ|