众所周知,在ES6之前JS中的变量作用域是函数级的,不具有块级作用域,而这句话也仅限于对Javascript语言本身而言的,聪明的开发人员可以通过闭包可以创建出伪块级作用域用来模拟这种其他语言一般都有的块级作用域.就像这样:
function foo(){ for(var a=0;a<10;a++){} console.log(a);//10 for(a=0;a<10;a++){} } foo();
function foo(){ (function(){ for(var a=0;a<10;a++){} })(); console.log(a);//undefined } foo();
然而es2015给你提供了更好的方案:
function foo(){ "use strict"; for(let a=0;a<10;a++){} console.log(a); //undefined } foo();
但写本文的主要目的与上面的关系不是太大,本文主要想探究函数级的作用域问题
function foo(){ var a=0; function b(){ console.log('b'); } function bar(){ console.log(a);//a b();//b } bar(); } foo();
执行这一段程序之后可以发现函数里的所有变量和函数是可以被该函数以及该函数内部的子函数所访问的,稍微改一下代码
function foo(){ var a=0; function b(){ console.log('b'); } function bar(){ console.log(a);//a b();//b } bar.call(d); } function d(){ } foo();
发现输出的结果是相同的,上面这个例子是为了证明解释器在查找变量的时候不是通过this定位查找的,因为在第一个例子中this为window(strict mode 为undefined),第二个例子中为d函数,这个现象的具体原因可以通过观察实际的作用域链去解释.
这段程序在运行到bar()时会将foo()指针压栈,开始执行bar,但解释bar发现有在foo中的同名变量c,这样就会将bar中使用的变量形成一个闭包,在作用域中同时会出现自己的c和闭包中的c.
很多人包括我在这个地方就会有一个疑问:闭包包的是引用变量还是整个函数?
不管闭包包了引用变量还是整个函数,对与开发者而言并没有什么两样,因为你就算只剩下引用部分剩余都被回收了我也不会受什么影响,而这个问题的影响范围只是GC是不是会部分回收,我想大部分开发者并不会去关心这个问题.然而这件事请其实是非常严重的,因为现有的GC机制是只要引用存在,整个函数就不能被GC回收,所以实际情况下闭包是包的整个函数,但你并不能访问到不存在引用的成员,这也非常符合常理.
通过以上的分析,我们就可以解释下面这个稍微复杂一点的例子的作用域范围了
function Foo(){ var a=1; this.a=2; function geta(){ "use strict"; console.log(a); //1 console.log(this.a); //Cannot read property 'a' of undefined } this.geta=function(){ console.log(a);//1 console.log(this.a);//2 } geta(); } foo=new Foo(); foo.geta();
总体来说,可以简单概括成一句话:this是this,作用域是作用域.不能将this和作用域混淆,这样以来哪些可以访问到,哪些不能被访问,很容易就能区分出来,也能很快理解为啥好多其他人写的程序里面会出现apply或call,这肯定是因为里面用到了this,而和作用域就扯不上任何关系了.