前端-阶段一所谓闭包_第1页
前端-阶段一所谓闭包_第2页
前端-阶段一所谓闭包_第3页
前端-阶段一所谓闭包_第4页
前端-阶段一所谓闭包_第5页
已阅读5页,还剩83页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

所谓闭包张立理

functioninfiniteSequence(){

vari=0;

return

function(){

return

i++;}}varincrement=infiniteSequence();console.log(increment());

console.log(increment());console.log(increment());//…WARNING!!WARNING非常非常的学术性大量术语词汇出没也许永远都用不上内容并非标准,有所删减逻辑不是那么清晰只谈函数,不提eval,不提newFunctionSummary引言什么是变量闭包之表象闭包之内在关于垃圾回收作用域的历史ECMAScriptv3ScopeChainVariableObjectIdentifierResolutionECMAScriptv5LexicalEnvironmentVariableEnvironmentGetIdentifierReference什么是变量变量是一种关联关系(association)关联的目标:符号名(symbolicname)值(value)关联是单向的–永远不可能根据值找到变量Asymbolicnameassociatedwithavalueandwhoseassociatedvaluemaybechanged.--WikipediaIdentifiernameValue‘GrayZhang’什么是变量VariableStatementvarIdentifier=AssignmentExpressionFunctionDeclarationfunctionIdentifier(FormalParameterList){

FunctionBody

}FormalParameterListIdentifier,Identifier[,…]什么是变量varname=‘GrayZhang’;functionadd(x,y){

returnx+y;

}KeywordIdentifierValue(StringLiteral)闭包之表象内层函数可以使用外层函数作用域内的变量functionouter(){varname=‘GrayZhang’;functioninner(){console.log(‘Hello‘+name);}inner();}外层内层闭包之内在Q:为什么javascript会有闭包?A:因为ECMAScript中变量解析是一个查找过程,而非绑定过程。Q:变量存放在哪里?A:ExecutionContext中的VariableEnvironment。Q:从哪里查找变量?A:ExecutionContext中的LexicalEnvironment。Q:如何查找变量?A:自内向外。?可执行代码(ExecutableCode)GlobalCodeFunctionCodeEvalCode<script>varname=‘GrayZhang’;varprefix=‘Hello‘;varphrases=[prefix,name];console.log(phrases.join(‘‘));</script>varsource=

‘varx=3;’+

‘console.log(x);’eval(source);functionsayHello(name){varprefix=‘Hello’;varphrases=[prefix,name];console.log(phrases.join(‘‘));}functiongetName(){varinput=$(‘#name’);returninput.val();}getName();执行环境(ExecutionContext)当进入(开始执行)一段可执行代码时,生成一个执行环境对象。执行环境对象通过栈(Stack)维护。新建的执行环境对象称为“当前运行的执行环境对象”。functionenterCode(code){

varec=newExecutionContext();

control.ecStack.push(ec);

control.runningEC=ec;

control.execute(code);

}执行环境(ExecutionContext)一个执行环境对象包括:词法环境–LexicalEnvironment变量环境–VariableEnvironmentThis绑定-ThisBindingExecutionContext:{LexicalEnvironment,VariableEnvironment,ThisBinding}词法环境(LexicalEnvironment)既是一个属性,又是一个类型。每个执行环境对象都有且仅有一个关联的词法环境对象。在代码执行过程中,需要解析变量时,通过词法环境对象进行解析,从其环境数据中得到值。一个词法环境对象包括:环境数据–environementrecords外层环境–outerenvironment词法环境(LexicalEnvironment)存在2种词法环境的实现类型DeclarativeEnvironmentObjectEnvironment区别是ObjectEnvironment可接受指定的对象作为环境数据属性的值?什么情况会出现ObjectEnvironment变量环境(VariableEnvironment)每个执行环境对象都有且仅有一个关联的变量环境对象。变量环境仅仅是一个名字,变量环境对象的类型是词法环境(LexicalEnvironment)。在进入(开始执行)代码时,所有的变量标识符(Identifier)会存放在当前的变量环境对象中。变量环境中有环境数据属性,但不使用外层环境属性。环境数据(environmentrecords)存在于词法环境或变量环境中。包含一个bindingobject,简单地认为bindingobject是一个Map对象,保存变量标签符(Identifier)和变量值(Value)的关系。常用方法:hadBinding(name)–查看是否有变量绑定createBinding(name)–创建一个变量绑定setBinding(name,value)–修改变量绑定的值getValue(name)–获取变量绑定的值deleteBinding(name)–删除一个变量绑定环境数据(environmentrecords)EnvironmentRecords:{bindingObject:{},hasBinding:function(name){return(nameinthis.bindingObject);},createBinding:function(name){this.bindingObject[name]=undefined;},setBinding:function(name,value){this.bindingObject[name]=value;},//…}环境数据(environmentrecords)存在2种环境数据的实现类型DeclarativeEnvironmentRecordsObjectEnvironmentRecordsLexicalEnvironmentEnvironmentRecordsObjectEnvironmentDeclaractiveEnvironmentObjectEnvironmentRecordsDeclaractiveEnvironmentRecords创建词法环境NewDeclarativeEnvironment(e)用于创建一个DeclarativeEnvironment进入函数时执行catch表达式时NewObjectEnvironment(o,e)用于创建一个ObjectEnvironment执行with表达式时创建词法环境functionNewDeclarativeEnvironment(e){varenv=newLexicalEnvironment();varenvRec=newEnvironmentRecords();envRec.bindingObject={};env.environmentRecords=envRec;env.outerEnvironment=e;returnenv;}functionNewObjectEnvironment(o,e){varenv=newLexicalEnvironment();varenvRec=newEnvironmentRecords();envRec.bindingObject=o;env.environmentRecords=envRec;env.outerEnvironment=e;returnenv;}消化一下名词解释完了吗?NO总结ExecutionContext:{LexicalEnvironment,VariableEnvironment,ThisBinding}EnvironmentRecords:{hasBinding(name),createBinding(name),setBinding(name,value),getValue(name),deleteBinding(name)}LexicalEnvironment:{environmentRecords,outerEnvironment}函数(Function)是一个对象(Object)。包含几个特殊的属性[[Construct]]–newSomeFunction()[[Call]]–someFunction()[[HasInstance]]–oinstanceofSomeFunction[[Scope]]–闭包[[FormalParameters]]–参数列表[[Code]]–可执行代码包含可执行代码(ExecutableCode)和执行状态(State)。创建函数(CreateFunctionObject)新建一个普通对象(newObject())将[[Class]]设为”function”将[[Prototype]]指向Ftotype根据默认的规则,设置[[Call]]、[[Contruct]]及[[HasInstance]]属性将[[Scope]]设置为当前的LexicalEnvironment对象设置[[Code]]、[[FormalParameterList]]及name、length、prototype属性创建函数(CreateFunctionObject)varfn=newObject();//[[DefaultValue]],[[HasProperty]],etc...initializeAsObject(fn);fn.[[Class]]='function';fn.[[Prototype]]=Ftotype;fn.[[Call]]=function(){/*...*/};fn.[[Construct]]=function(){/*...*/};fn.[[HasInstance]]=function(){/*...*/};fn.[[Scope]]=control.runningEC.lexicalEnvironment;fn.[[Code]]=functionBody;fn.[[FormalParameterList]]=parameterList;=functionName;fn.length=parameterList.length;totype={constructor:fn};创建函数(CreateFunctionObject)作用域([[Scope]])是函数对象的一个属性functionhello(){varo={};=‘GrayZhang’;returno;}varperson=hello();console.log();functionouter(){varname=‘GrayZhang’;functionsay(){alert(name);}returnsay;}varinner=outer();//inner.[[Scope]]inner();进入函数(EnteringFunctionCode)新建一个执行环境。根据规则设置this绑定。如果thisArg是null或undefined,设置为global。如果thisArg不是Object,设置为ToObject(thisArg)。以函数的[[Scope]]属性为参数,NewDeclarativeEnvironment创建一个LexicalEnvironment对象。将当前LexicalEnvironment设置为该值。将当前VariableEnvironment设置为该值。开始初始化参数及函数内声明的变量。同一对象进入函数(EnteringFunctionCode)varec=newExecutionContext();if(thisArg==null){thisArg=global;}if(typeofthisArg!=='object'){thisArg=ToObject(thisArg);}ec.thisBinding=thisArg;varlocalEnv=NewDeclarativeEnvironment(fn.[[Scope]]);ec.lexicalEnvironment=localEnv;ec.variableEnvironment=localEnv;initializeBinding(fn.[[Code]],fn.[[FormalParameterList]]);进入函数(EnteringFunctionCode)执行函数时,在作用域([[Scope]])的基础上添加词法环境(LexicalEnvironment)GlobalEnvironmentOuterEnvironmentCurrentEnvironment//GlobalEnvironmentfunctionouter(){//OuterEnvironmentfunctioninner(){//CurrentEnvironment}}varinner=outer();//[[Scope]]===OuterEnvironmentinner();定义绑定初始化

(DeclarationBindingInstantiation)从HostingBehavior说起……functionsayHello(name){if(!name){thrownewError();}else{varprefix='Hello';alert(prefix+name);}}functionsayHello(name){

varprefix;if(!name){thrownewError();}else{prefix='Hello';alert(prefix+name);}}定义绑定初始化

(DeclarationBindingInstantiation)遍历FormalParameterList(参数列表),对每一项(参数),如果VariableEnvironment中不存在,则添加并赋值。依次遍历源码中每个FunctionDeclaration(函数声明),对每一项(函数),如果VariableEnvironment中不存在,则添加,随后赋值。如果VariableEnvironment中不存在arguments,则添加并赋值。依次遍历源码中每个VariableDeclaration(变量声明),对每一项(变量),如果VariableEnvironment中不存在,则添加并赋值为undefined。定义绑定初始化

(DeclarationBindingInstantiation)functionformat(template,data){varregex=/\{(\w+)\:(\w+)\}/g;functionreplacer(match,name,type){varvalue=data[name];switch(type){case'boolean':value=!!value;break;case'html':value=encodeHTML(value);break;}returnvalue;}varhtml=template.replace(regex,replacer);returnhtml;}VariableEnvironmentarguments消化一下还有完没完了!NO变量查找GetIdentifierReference(lex,name)从给定的LexicalEnvironment中查找是否存在该变量如果不存在,则从LexicalEnvironment的OuterEnvironment中查找依次进行,直到OuterEnvironment为null,则返回undefined返回一个Reference对象,通过GetValue进一步获取变量的值变量查找functionGetIdentifierReference(lex,name){if(lex==null){returnnewReference(name,undefined);}varenvRec=lex.environmentRecords;if(envRec.hasBinding(name)){returnnewReference(name/*name*/,envRec/*base*/);}returnGetIdentifierReference(lex.outerEnvironment,name);}functionGetValue(reference){varenvRec=reference.base;returnenvRec.getValue();}大串烧进入全局环境创建全局执行环境并入栈创建全局环境对象全局的词法环境对象指向该对象全局的变量环境对象指向该对象在变量环境中添加outer绑定并赋值在变量环境中添加prefix绑定在变量环境中添加inner绑定//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();大串烧创建outer函数创建一个对象设置[[Call]]、[[Construct]]、[[HasInstance]]等设置[[Scope]]为当前词法环境–全局环境设置[[Code]]、[[FormalParameterList]]等设置length、prototype等//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();大串烧GlobalEnvironmentouter:{[[Scope]]}inner:undefinedprefix:undefinedGlobalExecutionContextVariableEnvironmentLexicalEnvironment大串烧为prefix变量赋值在全局环境中寻找name绑定–找到得到上一步返回的Reference的base

–即全局环境的环境数据对象调用其setBinding(‘prefix’,‘Hello’)//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();大串烧GlobalEnvironmentouter:{[[Scope]]}inner:undefinedprefix:‘Hello‘GlobalExecutionContextVariableEnvironmentLexicalEnvironment大串烧执行outer函数创建执行环境并入栈创建一个词法环境–DeclarativeEnvironmentouter的词法环境对象指向该对象outer的变量环境对象指向该对象在变量环境中添加say绑定并赋值在变量环境中添加name绑定//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();大串烧创建say函数创建一个对象设置[[Call]]、[[Construct]]、[[HasInstance]]等设置[[Scope]]为当前词法环境–outer的词法环境设置[[Code]]、[[FormalParameterList]]等设置length、prototype等//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;

functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();大串烧GlobalExecutionContextGlobalEnvironmentouter:{[[Scope]]}inner:undefinedprefix:‘Hello‘VariableEnvironmentLexicalEnvironmentOuterEnvironmentsay:{[[Scope]]}name:undefinedOuterExecutionContextVariableEnvironmentLexicalEnvironment大串烧为name变量赋值在outer的词法环境中寻找name绑定–找到得到上一步返回的Reference的base

–即outer的词法环境的环境数据对象调用其setBinding(‘name’,‘GrayZhang’)//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();大串烧GlobalEnvironmentouter:{[[Scope]]}inner:{[[Scope]]}prefix:‘Hello‘OuterEnvironmentsay:{[[Scope]]}name:‘GrayZhang’GlobalExecutionContextVariableEnvironmentLexicalEnvironmentOuterExecutionContextVariableEnvironmentLexicalEnvironment大串烧返回并赋值给inner变量将outer的ExecutionContext出栈在全局环境下寻找inner绑定–找到得到上一步返回的Reference的base–即全局环境的环境数据对象调用其setBinding(‘inner’,&say);//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();大串烧GlobalEnvironmentouter:{[[Scope]]}inner:{[[Scope]]}prefix:‘Hello‘OuterEnvironmentsay:{[[Scope]]}name:‘GrayZhang’GlobalExecutionContextVariableEnvironmentLexicalEnvironment大串烧执行inner函数创建执行环境并入栈创建一个词法环境–DeclarativeEnvironmentinner的词法环境对象指向该对象inner的变量环境对象指向该对象在变量环境中添加message绑定//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();大串烧GlobalEnvironmentouter:{[[Scope]]}inner:{[[Scope]]}prefix:‘Hello‘OuterEnvironmentsay:{[[Scope]]}name:‘GrayZhang’InnerEnvironmentmessage:undefinedGlobalExecutionContextVariableEnvironmentLexicalEnvironmentInnerExecutionContextVariableEnvironmentLexicalEnvironment大串烧为message变量赋值查找prefix变量的值在inner的词法环境中寻找prefix绑定–没有在outer的词法环境中寻找prefix绑定–没有在全局环境中寻找prefix绑定–找到取得prefix的值查找name变量的值…在inner的词法环境中寻找message给message绑定赋值//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();大串烧GlobalEnvironmentouter:{[[Scope]]}inner:{[[Scope]]}prefix:‘Hello‘OuterEnvironmentsay:{[[Scope]]}name:‘GrayZhang’InnerEnvironmentmessage:‘HelloGrayZhang’GlobalExecutionContextVariableEnvironmentLexicalEnvironmentInnerExecutionContextVariableEnvironmentLexicalEnvironment大串烧获取inner的值在inner的词法环境中寻找message绑定–找到得到上一步返回的Reference的base

–即inner的词法环境的环境数据对象调用该对象的getValue(‘message’)获取alert的值…将inner作为参数,调用alert函数//script…varprefix=‘Hello‘;functionouter(){varname=‘GrayZhang’;functionsay(){varmessage=prefix+name;alert(message);}returnsay;}varinner=outer();//inner.[[Scope]]inner();alert从何而来?大串烧functionborn(){varname='unknown';varage=1;return{setName:function(value){name=value;},grow:function(){age++;},print:function(){varparts=[name,age];varjoint='isnow';alert(parts.join(joint));}};}vargod=born();god.setName(‘leeight’);god.grow();god.grow();god.print();总结相关概念可执行代码–ExecutableCode执行环境–ExecutionContext词法环境–LexicalEnvironment变量环境–VariableEnvironment环境数据–EnvironmentRecords总结过程创建函数–[[Scope]][[Scope]]在创建时决定且不会变化进入函数–执行环境+词法环境+变量环境执行时在最内层增加词法环境定义绑定初始化–参数+函数声明+变量声明变量环境和词法环境是同一个对象变量查找–GetIdentifierReference延词法环境自内向外查找继续消化我以为我懂了,直到……HowwithworksHowcatchworksHowletworksWhencodemeetsevalWhencodemeetsnewFunctionWhenthereisstrictmode从代码说起functionouter(){varo=LargetObject.fromSize('400MB');returnfunction(){console.log('inner');};}varinner=outer();//对象图此时对象之间的引用关系?GlobalFunctionLexicalEnvironmentEnvironmentRecordsBindingObjectLargeObjectinner[[Scope]]environmentRecordsbindingObjectoGlobal和o有间接引用,无法回收o但是事实上……functionouter(){vari=3;

returnfunction(){debugger;};}varinner=outer();inner();javascript引擎有能力回收i如果你是计算机……functionouter(){vari=3;varj=4;vark=5;functionprepare(){i=i+k;}functionhelp(){i=i+j;}

prepare();returnfunction(){help();console.log(i);};}varinner=outer();inner();i:不可回收j:不可回收k:可回收prepare:可回收help:不可回收人类的智商计算机的智商压力好大~测试方法用断点!Chrome/Firefox看内存!IE/Opera一些基本结果IE6–8没有回收闭包内变量的机制Opera没有回收闭包内变量的机制Chrome回收闭包内变量后,再次访问该变量将抛出ReferenceErrorFirefox回收闭包内变量后,再次访问该变量会得到undefinedChrome、Firefox和IE9回收闭包内变量的策略基本相同试问!有哪些因素可能导致变量无法回收?变量被返回的函数直接引用。变量被返回的函数间接引用(通过嵌套函数)。返回的函数中有eval。返回的函数在with表达式建立的作用域中。返回的函数在catch表达式中。只谈结果,不谈过程!直接引用EngineCollectableChrome–V8NOFirefox–SpiderMonkeyNOIE9-ChakraNOfunctionouter(){vari=3;returnfunction(){i;};}varinner=outer();间接引用EngineCollectableChrome–V8NOFirefox–SpiderMonkeyNOIE9-ChakraNOfunctionouter(){vari=3;functionhelp(){i;}returnfunction(){help();};}varinner=outer();嵌套函数的平衡functionouter(){vari=0;functionhelp(){i++;}help();returnfunction(){console.log('nothing');}}varinner=outer();functionouter(){vari=0;functionhelp(){i++;returninner();}functioninner(){returni>3?i:help();}returninner();}varinner=outer();需要图的遍历需要处理环引用高成本+低效嵌套函数的平衡EngineCollectableChrome–V8NOFirefox–SpiderMonkeyNOIE9-ChakraNOfunctionouter(){vari=3;functionhelp(){i;}returnfunction(){};}varinner=outer();大恶魔evalfunctionouter(){vari=3;returnfunction(){returneval(‘i’);}

}varinner=outer();varresult=inner();console.log(result);//3?由字符串从词法环境中获取对象的唯一途径可变性特殊性大恶魔evalvarreference=eval(‘someObject’);字符串分析varreference=eval(‘some’+‘Object’);常量预计算vars=‘some’;varreference=eval(s+‘Object’);变量->常量替换vararray=[‘some’,‘ject’];varreference=eval(array.join(‘Ob’));大恶魔evalfunctionouter(){vari=3;returnfunction(variableName){returneval(variableName);}

}varinner=outer();varinput=document.getElementById(‘variable_name’);varname=input.value.trim();varresult=inner(name);console.log(result);//3囧TooSimple,SometimesNative.大恶魔evalEngineCollectableChrome–V8NOFirefox–SpiderMonkeyNOIE9-ChakraNOfunctionouter(){vari=3;returnfunction(){eval(‘’);//无论eval的内容是什么};}varinner=outer();间接eval和newFunction间接evalwindow.eval(coe)|(1,eval)(code)|(true&&eval)(code)InEdition5,indirectcallstotheevalfunctionusetheglobalenvironmentasboththevariableenvironmentandlexicalenvironmentfortheevalcode.newFunctionReturnanewFunctionobjectcreatedasspecifiedin13.2passingPastheFormalParameterListandbodyastheFunctionBody.PassintheGlobalEnvironmentastheScopeparameterandstrictastheStrictflag.间接eval和newFunctionvari=3;functionouter(){vari=4;returnfunction(){returnwindow.eval('i');/**varfn=newFunction('returni;');*returnfn();*/}}varinner=outer();varresult=inner();console.log(result);//3X间接eval和newFunctionEngineCollectabl

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论