# 作用域和闭包 ECMAScript5;js的代码没有代码块;使用函数运行的机制进行创建闭包;闭包就是作用域的意思;ES5中,JS中只有函数才可以创建能操作的作用域; JavaScript中的内存也分为栈内存和堆内存。一般来说,栈内存中存放的是存储对象的地址,而堆内存中存放的是存储对象的具体内容。对于原始类型的值而言,其地址和具体内容都存在与栈内存中;而基于引用类型的值,其地址存在栈内存,其具体内容存在堆内存中。堆内存与栈内存是有区别的,栈内存运行效率比堆内存高,空间相对推内存来说较小,反之则是堆内存的特点。所以将构造简单的原始类型值放在栈内存中,将构造复杂的引用类型值放在堆中而不影响栈的效率。 - 函数执行时形成私有作用域 - 全局变量和私有变量 - 如何区分函数中出现的变量是私有的还是非私有的 - 如何查找当前作用域的上级作用域? - JavaScript中的顶级作用域是谁? - 变量和属性有什么关系 - 函数运行产生的作用域 - 闭包 - 作用域不销毁的情况|内存释放 ##### 函数执行时形成私有作用域 函数执行的时候(直接目的:让函数体中的代码执行)会形成一个新的私有的作用域(栈内存),供函数体中的代码执行; - 1)给形参赋值 - 2)私有作用域下的预解释 - 3)私有作用域下的代码执行 形成的新的私有的作用域还保护了里面的私有变量不受外界的影响,我们把函数的这种保护机制叫"闭包":为什么要有作用域;因为变量要规定活动范围的,为便于管理不同范围的变量;所以要给变量设置活动范围; 预解释也是在各自的作用域里进行预解释的; function fn(){ var a=1; } fn(); fn(); console.log(a);//Uncaught ReferenceError: a is not defined; > 因为a没有声明和定义过,所以报错了; 上面的fn运行了两次,所以产生了两个堆内存;两个作用域(作用域也就是闭包)各自分别有一个a的变量;a的值都是数字1,但是两个变量是不相等的;两个a之间是没有任何关系的; 就好比我们都属于人类;我们都继承了人类这个对象所具有的特征;我有一双手,你也有一双手,但是我们两个人的手是没有关系的;我的手不等于你的手; ##### 全局变量和私有变量 - 在全局作用域下声明的变量是全局变量; - 在私有作用域中声明的变量是私有变量;函数的形参也是私有的变量; ##### 如何区分函数中出现的变量是私有的还是非私有的? 首先看是否为形参,然后看是否在私有作用域中声明过(有没有var过),两者有其一就是私有的变量,那么在当前函数中不管什么位置出现都是私有的,和全局的没有半毛钱的关系;如果两者都没有,说明不是私有的;如果一个函数中出现的变量不是私有的,那么会往其上级作用域查找,上级没有则继续查找,一直找到window为止,如果window也没有呢? > 如果是获取:会报错 function fn() { console.log(num);//Uncaught ReferenceError: num is not defined } fn(); > 如果是设置:不是私有的,找全局,全局没有的话相当于给全局加一个 function fn() { num = 13;//相当于给window增加了一个叫做num的属性名,属性值是13 window.num=13; } fn(); console.log(num);//13 ##### 如何查找当前作用域的上级作用域? **当前作用域的上级作用域是谁和函数在哪执行的没有任何的关系,我们只需要看当前函数是在哪个作用域下定义的,那么它的上级作用域就是谁;** 下面是查找上一级作用域,一直找到window的案例; var total = 100; function fn() { var total = 10; return function () { console.log(total); }//如果返回的是一个引用数据类型的值(对象、数组、函数...),首先是把这个值开辟一个内存空间,有一个地址,然后把内存地址返回 ->例如在这里其实返回的就是 return xxxfff111; } var f = fn();//f=xxxfff111; f(); //输出的结果是 10 还是 100 ? 为什么 ? 如果是在自执行函数里呢? var total = 100; function fn() { var total = 10; return function () { console.log(total); } } var f = fn();//f=xxxfff111; ~function () {   var total = 1200;   f();//->;里面变量的上级作用域是谁 }(); 下面代码输出的是什么? var a=0; var b="0"; function fn(){ console.log(a);   console.log(b);   console.log(typeof a); console.log(typeof b);   a="1";   var b=1;   console.log(a);   console.log(b);   console.log(typeof a);   console.log(typeof b); } fn();//运行后会输出什么?如果没有这行代码;函数fn的定义是没有意义的;函数只声明定义,而不运行是没有任何意义的; //0,undefined,"number","undefined","1",1,"string","number" 预解释是作用域中的预解释;js里是可以函数里面套函数的;都运行的时候,是在函数创建的作用域里再创建一个作用域; > 下面是作用域的范围; var a="window"; console.log(a);//window function father(){   console.log(a);//undefined   var a="father";   console.log(a);//father   function children(){   console.log(b);//undefined   console.log(a);//father   var b="flag";   a="children";   console.log(a);//children  }  children();  console.log(a);//children } father(); console.log(a);//window  JavaScript中的代码执行顺序是从上到下逐条运行的;遇到function定义函数的代码块;直接跳过;遇到函数执行的代码;就找到引用的函数地址;开始跳到执行函数产生的作用域中执行代码;等函数执行完以后,再回到当前作用域执行下面的代码; ![](http://i.imgur.com/DAMkRA7.jpg)  上面的代码运行后,输出的是 > "window",undefined,"father",undefined,"father","children","children","window" **作用域链查找**:当作用域套作用域的时候,children内找不到某个变量;会到children的父作用域father中找;当father中找不到的时候;会到father的父作用域找;一直找到window这个根作用域;属于作用域链式查找; JavaScript中不带var声明变量,变量会冒泡到上一级作用域中;也就是说会污染到上一级作用域;但是对上上级的作用域是没有影响的;很多教材和视频上说不带var声明的变量;变量会变成全局变量,这个说法是不全面的; 【带var和不带var的区别和关系】 只有在全局作用域下才有这样的关系 区别:带var的可以在代码执行前进行声明,而不带var的不能提前的声明;例子如下; //预解释:var num1; console.log(num1);//->undefined var num1 = 12; console.log(num2);//->Error:num2 is not defined num2 = 12; 关系:在全局作用域下,var num1=12,不仅仅是增加了一个全局变量,而且还相当于给window增加了一个叫做num1的属性名 -> window.num1=12; var num1 = 12; console.log(num1);//12 console.log(window.num1);//12 num2 = 13; console.log(num2); console.log(window.num2); //我们可以把window.省略; > 下面是函数内带var和不带var的; console.log(total); //undefined var total = 0; function fn() { console.log(total); //undefined  total += 100; var total = 200;//total是被保护的;  console.log(total); //200 } fn(); console.log(total); //0 下面是不带var的 console.log(total);//undefined var total = 0; function fn(num1, num2) { console.log(total);//0 ->total不是私有的,找全局下的total,也就是在这里出现的所有的total其实应该都是全局的 total = num1 + num2;//->全局的total=300 console.log(total);//300 } fn(100, 200); console.log(total);//300 ### JavaScript中的顶级作用域是谁? 顶级作用域是Global;数据引用高程三133页; 目前只有nodeJs这个语言可以访问到global;nodeJs中是进程;叫process;后台编程中是不管浏览器的事的,node中是global;JavaScript中只可以访问到window; window相当于global的代理人;好比上帝和教皇的关系;一般在JS中说的全局变量都是window;全局变量就是window的属性;通过window.varName可以访问到; 但并不是所有的都属于window的,比如Object,Math,String,Function等这6个包装类,就不属于window的属性;和window是无关的;类似于中国党政军的关系;互相独立的; window只是前端编程里的顶级作用域; ### 变量和属性有什么关系 全局变量是window的属性;window的属性也可以看做全局变量;一个对象的属性,也可以看做以这个对象为作用域下的变量with语句;   如下: