1.作用域作用域是可访问变量的集合。在JavaScript中,对象和函数也是变量。在JavaScript中,作用域是可访问的变量、对象和函数的集合。JavaScript函数作用域:作用域在函数内部被修改。常见作用域分为:全局作用域(window,global)函数作用域(function)块级作用域({})词法作用域(this)函数作用域变量在函数内部声明,变量是局部作用域,也称为函数作用域.局部变量:只能在函数内部访问。例如:functionfn(){leta=1}fn()console.log(a)//erroranotdefined可以看出,全局访问这个变量a时,会报错,说明全局无法访问函数内部的变量(闭包除外)。因为局部变量只适用于函数,所以不同的函数可以使用同名变量。局部变量在函数开始执行时创建,并在函数执行后自动销毁。如果要读取函数中的变量,必须使用return或closurefunctionfn(){varb=2returnb}fn()console.log(b)VM879:62全局作用域变量定义在函数外,即对于全局变量。全局变量具有全局作用域:网页中的所有脚本和函数都可以使用。leta=1functionfn(){console.log(a)}fn()console.log(a)//print11//修改函数中的变量a,因为变量a是全局的,所以会打印22leta=1functionfn(){a=2console.log(a)}fn()console.log(a)//22如果变量没有在函数中声明(没有使用var关键字),变量是一个全局变量。functionfn(){a=2console.log(a)}fn()console.log(a)//22*注意:未在函数内部声明的变量以window和global的形式存在,具有全局作用域,但是this变量可以删除,但是全局作用域不能块级作用域ES6引入了let和const关键字。与var关键字不同,在花括号中使用let和const声明的变量存在于块级Scope中,这些变量不能在花括号之外访问。{lett1=1constt2=2vart3=3console.log(t1,t2,t3)}console.log(t1,t2,t3)//t1t2报错t3打印3lexicalscopelexicalscope,and称为静态作用域,变量是在创建时确定的,而不是执行阶段。变量的作用域只能在执行阶段确定,即动态作用域vara=12;functionfoo(){console.log(a)//12}functionbar(){vara=13;foo();}bar()因为JavaScript遵循词法(静态)作用域,同级的foo和bar不能访问对方块作用域中的变量,所以foo在执行的时候没有找到变量a,会找对于它从上层来说,foo的外层是window,所以找到a=12,所以输出12。2.作用域链JS往下找当前作用域内的变量,如果没有找到,再到它的上层作用域,以此类推,直到找到变量或者已经到达全局作用域,如果还是找不到全局作用域更改变量,然后在全局范围内隐式声明变量(在非严格模式下)或直接报错。就像上面的例子,稍微修改变量,值就会不一样vara=12;functionfoo(){console.log(this.a)//13}functionbar(){this.a=13;foo();}bar()具体分析:foo函数找不到里面的变量a,this的指向是调用b1时确定的,在函数bar的上层作用域找到函数foo中的this指向是函数bar,在函数bar中找到变量a,所以this.a为13。如果在函数bar中没有找到变量a,则在上层作用域,即全局作用域中寻找。如果还是找不到,就会报错。3.闭包介绍闭包是可以读取其他函数内部变量的函数。比如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解为“函数内部定义的函数”。本质上,闭包是连接函数内部和函数外部的桥梁。一个作用是可以在函数内部读取前面提到的变量,另一个是在内存中时刻保存这些变量的值。可以看看阮一峰的文章例子functionf1(){varn=999;nAdd=function(){n+=1}functionf2(){alert(n);}返回f2;}var结果=f1();结果();//999nAdd();结果();//1000在这段代码中,result实际上是闭包f2函数。运行了两次,第一次的值为999,第二次的值为1000。这证明函数f1中的局部变量n一直保存在内存中,并没有在调用f1后自动清除。为什么会这样?原因是f1是f2的父函数,而f2被赋值给了一个全局变量,导致f2一直在内存中,而f2的存在又依赖于f1,所以f1一直在内存中,不是在调用之后,被垃圾回收机制(garbagecollection)回收。这段代码中另一个值得注意的地方是“nAdd=function(){n+=1}”这一行。首先,nAdd前面没有使用var关键字,所以nAdd是全局变量,不是局部变量。其次,nAdd的值是一个匿名函数(anonymousfunction),而这个匿名函数本身也是一个闭包,所以nAdd相当于一个setter,可以在函数外对函数内的局部变量进行操作。使用注意事项(1)由于闭包会导致函数中的变量存放在内存中,内存消耗非常大,所以闭包不能滥用,否则会导致网页出现性能问题,以及可能会导致IE内存泄漏。解决方法是在退出函数之前删除所有未使用的局部变量。(2)闭包会在父函数外改变父函数内变量的值。因此,如果将父函数作为对象,将闭包作为其公共方法(PublicMethod),将内部变量作为其私有属性(privatevalue),那么一定要注意不要改变a的值父函数中的变量。作为思考题,大家可以想想这两段代码分别输出什么值。代码1:varname="TheWindow";varobject={name:"MyObject",getNameFunc:function(){returnfunction(){returnthis.name;};}};console.log(object.getNameFunc()());代码2:varname="TheWindow";varobject={name:"MyObject",getNameFunc:function(){varself=thisreturnfunction(){returnself.name;};}};console.log(object.getNameFunc()());分析:从上图可以看出,函数getNameFunc里面打印的this指向的是object对象,但是函数getNameFunc中返回的是一个匿名函数,而函数的This指向的是window,所以直接调用函数getNameFunc会直接打印全局变量“TheWindow”。但是如果你使用self.name打印,它会输出“MyObject”。object.getNameFunc的使用方式不同,输出的内容也不同returnfunction(){console.log(this)returnself.name;};}};console.log(object.getNameFunc()())//'我的对象'varjj=object.getNameFunc()()jj//'我的对象'varkk=object.getNameFunc()kk()//'MyObject'varll=object.getNameFuncll()()//'TheWindow'图
