前言:这是笔者学习以后本身的理解与整理。若是有错误或者疑问的地方,请你们指正,我会持续更新!javascript
做用域、做用域链、执行环境、执行环境栈以及 this 的概念在 javascript 中很是重要,本人常常弄混淆,这里梳理一下:java
JavaScript 没有块级做用域的概念,只有函数级做用域:变量在声明它们的函数体及其子函数内是可见的。函数
做用域就是变量和函数的可访问范围,控制着变量和函数的可见性与生命周期,在 JavaScript 中变量的做用域有全局做用域和局部做用域。性能
变量没有在函数内声明或者声明的时候没有带 var 就是全局变量,拥有全局做用域。学习
<script type="text/javascript"> function test1(){ a = 1;//全局变量,只有在当前函数运行时,才有效 } test1(); console.log(a);//1 注意test1函数必须运行,否则找不到a </script>
全局变量能够当作 window 对象的属性用,他们是同样的。优化
<script type="text/javascript"> var b = 1;//全局变量 console.log(b === window.b);//true 全局变量能够当作window对象的属性用,他们是同样的; </script>
window 对象的全部属性拥有全局做用域,在代码任何地方均可以访问。this
函数内部声明的变量就是局部变量,只能在函数体内使用,函数的参数虽然没有使用 var 但仍然是局部变量。spa
<script type="text/javascript"> var c = 1;//全局变量 // console.log(d);//ReferenceError: d is not defined 引用错误,当前做用域就是最外层做用域,依然找不到d function test2(d){ console.log(c);//1 全局变量,哪均可以访问;(先找当前做用域,找不到,就向外层做用域找,直到window最外层,找到了) console.log(d);//3 形参是局部变量,只有当前做用域下能够访问 } test2(3); </script>
做用域链取决于函数被声明时的位置,解析标识符的时候就先从当前做用域开始找,在当前做用域中没法找到时,引擎就会在外层嵌套的做用域中继续查找,直到找到该变量,或抵达最外层的做用域(也就是全局做用域)为止;它的路线已经被定死了,和函数在哪里运行无关;code
<script type="text/javascript"> var a = 1; var b = 2; var c = 3; var d = 4; function inner(d) {//它的做用域链是inner---全局 var c = 8; console.log(a);//1 当前做用域找不到a,去全局做用域找到了a=1 console.log(b);//2 当前做用域找不到b,去全局做用域找到了b=2 console.log(c);//8 当前做用域找到了c=8 console.log(d);//7 当前做用域找到了d=7,形参也是局部做用域 // console.log(e);//ReferenceError: e is not defined 引用错误,找不到e, 它的做用域链是inner---全局 console.log(a+b+c+d);//18 } function outter(e) { var a = 5;//inner()的做用域链是inner---全局,因此这个a至关于无效 var b = 6;//inner()的做用域链是inner---全局,因此这个a至关于无效 inner(7); } outter(999);//这个999无效,里面的e根本找不到 </script>
在多层的嵌套做用域中能够定义同名的标识符,这叫做“遮蔽效应”,内部的标识符“遮蔽”了外部的标识符对象
经过 window.a 这种技术能够访问那些被同名变量所遮蔽的全局变量。但非全局的变量若是被遮蔽了,不管如何都没法被访问到
<script type="text/javascript"> var a = 'Lily'; var b = 'Lucy'; function outer() { var b = 'Jesica'; var c = 'Susan'; function inner(c) { console.log(a);//Lily console.log(window.b);//Lucy console.log(b);//Jesica console.log(c);//Jenifer } inner('Jenifer'); } outer(); </script>
执行环境(execution context),也叫执行上下文。每一个执行环境都有一个变量对象(variable object),保存函数可访问的全部变量和数据(也就是函数的做用域链上的全部数据和变量)。咱们的代码访问不到它,它是给引擎使用的。
执行环境栈,当执行进入一个函数时,函数的执行环境就会被推入一个栈中。而在函数执行完以后,栈将其执行环境移除,它里面的变量和数据会被标记清除,等待垃圾回收,再把控制权返回给以前的执行环境。javascript 程序中的执行正是由这个机制控制着。
须要注意的是若是当前执行环境(存放当前做用域链里的数据和变量)找不到变量,那就是找不到了,不会往以前的那个执行环境查找,和做用域链是不同的;
代码的执行顺序也不全是一行一行的执行,而是和函数的调用顺序有关:
1 <script type="text/javascript"> 2 var a = 1; 3 var b = 2; 4 var c = 3; 5 var d = 4; 6 function inner(d) {//它的做用域链是inner---全局 7 var c = 8; 8 console.log(a);//1 当前做用域找不到a,去全局做用域找到了a=1 9 console.log(b);//2 当前做用域找不到b,去全局做用域找到了b=2 10 console.log(c);//8 当前做用域找到了c=8 11 console.log(d);//7 当前做用域找到了d=7,形参也是局部做用域 12 // console.log(e);//ReferenceError: e is not defined 引用错误,找不到e, 它的做用域链是inner---全局 13 console.log(a+b+c+d);//18 14 } 15 function outter(e) { 16 var a = 5;//inner()的做用域链是inner---全局,因此这个a至关于无效 17 var b = 6;//inner()的做用域链是inner---全局,因此这个a至关于无效 18 inner(7); 19 } 20 outter(999);//这个999无效,里面的e根本找不到 21 </script>
以上代码的执行顺序:
因为在做用域链上查找变量是须要消耗性能的,咱们应该尽快的找到变量,因此在函数多层嵌套的时候,咱们应尽量的使用函数内部的局部变量;
咱们在函数内部使用全局变量能够说是一种跨做用域操做,若是某个跨做用域的值在函数的内部被屡次使用,那么咱们就把它存储到局部变量里,这样能够提升性能。