首先给js的做用域这个话题打标签:2,var, 全局变量,局部变量,函数,undefined, 做用域提高,赋值不会提高,ReferenceError, 同名覆盖。
打完标签以后,咱们来讲跟做用域有关的几条铁打的规则:
1: JS的做用域有2
种:全局做用域,函数做用域。浏览器
把做用域想象成一个房间,而{}是房间的门。门上装了一个猫眼,因此房间里面能够看清楚外面,可是外面却看不见里面。 在JAVA或者C里面,大括号可能会出现的状况有两种: 1: 一个function定义的时候 2: 一个块定义(好比if,for, while)的时候. 因此此时的做用域有三种类型: 1: 全局做用域, 2: 函数做用域 3: 块级做用域。
可是在JS里面,虽然{}出现的状况也有两种,可是只有function的{}才起到栅栏的做用。闭包
2: 声明在全局做用域里的变量是全局变量
,声明在函数里面的变量是局部变量
。函数
3: 怎么创造一个全局变量和局部变量?学习
创造全局变量的方法有两种:spa
1: 在全局做用域内用var定义: var a; 2: 声明一个变量,不带var(不管是在全局,在函数里面仍是在一个块里面):b
创造局部变量的方法只有一种:3d
在函数体里面,带var声明一个变量: function func(){var b;}
3: 使用一个没有声明过的变量,会获得一个ReferenceError
。不管什么状况下。
4: 不一样做用域内,同名的变量,越小的做用域的变量会覆盖
越大的做用域的。 调试
4: 做用域提高:变量在声明以前就能够引用了!
这个不是和第三点矛盾了吗?其实并无。它背后的真正原理是:并非做用域被提高(咱们前面说了,一个变量的做用域会被框在一对栅栏{}里面,一旦这个栅栏肯定了,那这个做用域是不可能变化的),实际上是变量的‘声明’在其做用域里面被放到任何代码以前(固然包括引用它的代码以前)。看一段代码:code
var scope = 'global'; function func(){ console.log(scope); //输出‘undefined’,而不是‘global’ var scope = 'local'; console.log(scope); //输出‘local’ }
看到第一个console,可能觉得会输出‘global’, 由于经过猫眼能够看见外面的变量。可是,一旦咱们进到一个函数体里面,遇到任何的变量的引用,首先要先在当前的房间里面找,只有在当前的房间里面找不到时,才到父层去找。那为何是‘undefined
呢?其实以上的代码等价于:图片
var scope = 'global'; function func(){ var scope; //变量的声明会提高到最前面,可是赋值并不会,因此此刻scope的值还只是undefined. console.log(scope); //输出‘undefined’,而不是‘global’ scope = 'local'; //赋值在这里完成 console.log(scope); //输出‘local’ }
============闭包的分割线===========
1: 什么是闭包?
我看过看多不一样的书,对必包的定义都不同,并且就算是我知道了闭包的定义,对我真正理解它的工做原理仍是没有什么用。因此,我就不去纠结它究竟是什么,我接下来只关注它是怎么工做的。
2: 何时会造成闭包?
如下内容非原创,这里只是我本身的一个学习笔记。我看了http://www.jianshu.com/p/7312...(在这里感谢做者),跟着文章里面的例子(代码根据本身的喜爱改了一些)走一遍:
1: When? 闭包出现的时刻?ip
function foo() { var a = 2; function baz() { console.log( a ); } return baz; } var fn = foo() fn();//2
在断点的过程当中,运行完第9行代码的时候,调试窗口里并无出现任何闭包;直到我运行了第10行代码,跳到第5行的时候(也就是baz这个方法被调用的时候),调试窗口出现了闭包(Closure)。而且能够看到说foo是closure, 它包含一个变量a,值为2。
结论1:虽然不少书上说闭包跟函数定义的时候的做用域有关,跟它执行时候的做用域无关,可是它在浏览器里面出现的时机倒是在执行的时候。
2 How? 闭包出现的条件?
function foo() { var a = 2; function baz(m) { console.log(m); } return baz; } var fn = foo() fn(20); //20
不少地方都说在方法里面定义方法就会造成闭包,可是在这里例子里面,我执行完第10行的代码,调试窗口并无出现任何闭包。和例1的差异在于,baz没有引用变量a.
结论2:一个方法,必定要引用其父层方法(非本身方法内部的变量)的变量才会造成闭包。
可是闭包的造成是否是必定要执行到引用了父层做用域变量的方法才出现呢?看下面一个例子:
3: 当父层方法里有不仅一个方法
function foo() { var a = 10; var b = 30; function fn1() { return a; } function fn2() { return 20; } return fn2; } var fn = foo(); fn();
当我执行到17行而后跳进其方法体(第10行)的时候,调试窗口出现了Closure, 而且它有一个变量a,
值为10。其实这个例子说明了两个事情:
结论3: 在执行一个定义在方法里面的方法时,即便它的方法体本身没有引用父层变量,可是只要有任何兄弟方法引用了,那就会造成闭包。闭包里面保存的变量只有被方法引用了的变量(这个例子里,闭包里只有a,并无b)。
4: 当不是兄弟方法,而是子方法引用了父级变量,会发生什么状况呢?
function foo() { var a = 10; var b = 30; function fn1() { var c = 20; function fn2(){ return a; } return fn2; } return fn1; } var fn1 = foo(); var fn2 = fn1(); fn2();
fn1方法体内没有引用任何的父层变量,可是它的子方法fn2引用了变量a。在调试的过程当中,当执行到f1的时候(执行到第6行,这时候能够看到变量c都仍是'undefined'),Closure就已经出现了,并不像以前那样必定要等到执行fn2.最后看一个例子:
5: 当不是直系子方法,而是侄子方法引用了父级变量
function foo() { var a = 10; var b = 30; function fn1() { var c = 20; function fn2(){ return a; } return fn2; } function fn3(){ return 40; } return fn3; } var fn3 = foo(); fn3();
当执行到调用fn3的时候,Chosure出现了。因此闭包究竟是在何时造成呢?在咱们前面的结论里面有提到说方法执行的时候,可是通过后面的这几个例子说明并不必定非得是执行的时候,并且JavaScript是基于词法做用域的语言(变量的做用域在其定义的时候就已经肯定了,不依赖于执行时的环境),因此我倾向于闭包(Closure)是在方法定义的时候就造成了的。那最后来讲说What的问题:闭包究竟是什么?
1: 闭包是一个做用域。(鉴于在Chrome的调试窗口,Closure是放在Scope下面的)
2: 那闭包这个做用域是个什么范围:被后代方法(子方法,孙子方法。。。)所引用的变量所在的做用域(或者说这个变量所在的方法)。
3: 闭包里面有什么:只包含被后代方法所引用了的变量,并不包含这个变量的兄弟姐妹。
4: 上面的全部例子都只引用了变量,换成方法,也都是一样的结果。
真的是最后一个例子了: