做用域闭包
技术通常水平有限,有什么错的地方,望你们指正。异步
做用域就是变量起做用的范围。做用域包括全局做用域,函数做用域以块级做用域,ES6中的let和const能够造成块级做用域。函数
除了块级做用域,在函数外面声明的变量能够在任何一个地方被访问到,这些变量的做用域都是全局做用域,全局做用域中的变量能够再任何一个地方使用:spa
var a = "zt"; function fn1(){ console.log(a); } function fn2(){ console.log(a); } fn1(); fn2();
在函数里面声明的变量只能在当前函数内使用,这些变量的做用域咱们称为函数做用域,只在当前函数内有效:线程
function fn1(){ var a = "zt"; console.log(a); } function fn2(){ console.log(a) } fn1(); fn2();//报错提示a没有定义
函数内定义的变量只在当前函数内有效,在函数之外的地方是不能被访问到的,fn2函数内没有定义a,全局做用域中也没有a使用一个不存在的变量因此报错。code
做用域链blog
做用域是能够嵌套的好比在全局做用域里面建立一个函数,函数里面能够在建立一个函数,这样就发生了做用域的嵌套,做用域链能够把做用域连接起来。当使用一个变量的时候,会优先在当前做用域内去寻找变量,若是当前做用域内不存在就会去上层做用域去寻找一直到全局做用域,若是还不能找到变量就会报错。 作用域
var a = "global"; function fn1(){ console.log(a); } fn1();
做用域是静态的开发
咱们先看一个例子:io
var flag = "outer"; function demo(){ var flag = "inner"; function inner(){ console.log(flag); } return inner; } var fn = demo(); fn();//inner
var flag = "outer"; function demo(){ var flag = "inner"; fn(); } function fn(){ console.log(flag); } demo();//outer
经过这两个例子咱们能够看出函数的做用域是静态的,一个函数无论在哪被调用,它的做用域都是声明时的做用域。函数的做用域在声明时就已经被建立,在调用函数时会去访问他已经建立的做用域。
闭包
闭包在MDN中的定义为:闭包是指那些能够访问独立变量的函数,因此在定义上咱们能够把全部的函数都看作是闭包。闭包即密闭的空间,咱们能够很天然的想到函数,由于函数就会生成一个密闭的空间,若是函数想称为一个闭包只须要在使用一个外部变量便可(使用外部变量的函数就是闭包)。经过闭包能够给咱们带来一些便利,就是能够在高等级的做用域使用低等级做用域中的变量:
function demo(){ var flag = "test"; return function(){ console.log(flag); } } demo()();
咱们把demo函数里面的函数经过return使其能够在外部使用,咱们已经说过做用域都是静态的,这样咱们在外部使用return的函数时,就能够看到咱们在全局做用域中调用函数最后输出了demo函数里面的"test"。
这样咱们能够作一些更有意义的事:
var data = []; function demo(){ var data = []; return{ add:function(a){ data.push(a); }, print:function(){ console.log(data); } } } var tool = demo(); tool.add(1); tool.add(2); tool.add(3); tool.print();//[1, 2, 3]
咱们能够利用demo函数里面的data来存储咱们的信息并且不用担忧它被破坏(demo里面的data被私有化),并且咱们也能够在外部在声明一个同名的data来存储别的信息,这两个不会产生任何冲突。
闭包也能够帮咱们解决一些小问题:
for(var i=0;i<4;i++){ setTimeout(function(){ console.log(i); }); }
咱们预期的结果是打印当前循环的i值结果输出全是4。先解释一下出现这么状况的缘由:JS是一种单线程的语言,而setTimeout是异步的,只有当咱们的代码执行完成之后setTimeout的处理函数才会执行,而执行的时候i的值已是4了因此最终的输出全是4。
咱们能够经过闭包来解决这一问题:
for(var i=0;i<4;i++){ (function(i){ setTimeout(function(){ console.log(i) }) }(i)) }
闭包能够造成一个独立的做用域这样每次循环都会有一个独立的函数做用域,循环完成后虽然i的值仍然是4可是setTimeout的处理函数在寻找i的时候会优先找到做为参数的i,而每个参数i都表示当次循环的i,利用闭包咱们能够完美的解决这种问题。
在咱们实际开发的过程当中,遇到这种状况咱们就能够经过闭包来解决,咱们所说的"这种状况"一般有三个特色:
1.首先有一个循环
2.循环里面会建立函数,而且函数是延后执行的
3.这些延后执行的函数会使用一个共同的变量,而且这个共同的变量和当前的循环值有关系
咱们按照这个规律套一下上面的代码:
循环有了,每次循环也会生成一个函数,这些函数也都是在循环完成后才能执行,并且每个函数都使用共同的i,而i就是当前的循环值,正好符合咱们的三个特色。咱们经过(function(){}())这种方式(匿名函数自执行)来造成一个闭包达到咱们预期的目的。
更深层次的了解,能够在网上查阅相关资料。