对于javascript中作用域的一些理解
1 什么是作用域?
编程语言中为了让程序能够高效的,安全的存储和访问变量而设定的一套规则。(我理解是在javascript中作用域其实就是代码在某代码块中执行时变量进行(取值或赋值)查询时的规则)
2 编译型语言和解释型语言
2.1 编译型语言
使用专门的编译器,针对特定的平台,将高级语言源代码一次性的编译成可被该平台硬件执行的机器码,并包装成该平台所能识别的可执行性程序的格式。
特点:因为其提前编译为机器语言,运行时不依赖开发环境,因此效率相对较高。
举例:C、C++、Delphi、Pascal、Fortran
2.2 解释型语言
使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行。是代码在执行时才被解释器一行行动态翻译和执行(这个过程也称为编译),而不是在执行之前就完成翻译。
简单来说就是边编译边执行,因此他运行时依赖于解析器。
特点:只要平台安装相应的解析器就可以了,这样一套代码可以在不同的系统平台运行。具有跨平台的优势。
举例:Java、Basic、javascript、python
2.3 Javascript是解释型语言,javascript语言引擎在作用域的严格规则下利用编译器完成javaScript程序的编译和执行。
3 作用域的嵌套
当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。
3.1 全局作用域是javascript最上级的作用域
3.2 下级作用域变量执行查询时首先会从当前的作用域查找,没查到就会逐级往上进行查询,
作用域变量执行查询时只会向上查询不会往下查询或同等级查询。
如果一旦查到,就停止往上查询,获取该位置变量对该变量进行赋值或取值的操作。(如示例1中的变量a)。
如果查完全局作用域后还没查到就会抛出一个错误。(如示例1中的变量b)。
3.3 由于2的原因,所以所有的下级作用域都能使用全局作用域的中的变量和常量。(前提是变量查找要到达全局作用域),(如示例1中的变量c)。
注意1:函数作用域是由函数定义的位置确定的,这个容易跟this的指向弄混。
let a="a"; let c="c"; function bar(){ let a="bra_a"; console.log(c); //c 该值由查询到全局作用域后获取 function foo(){ console.log(a); //bra_a,这是一个取值操作,首先查询foo作用域没有,再查询bar作用域查到了就停止。 //console.log(b); // ReferenceError: b is not defined console.log(c); //c 该值由查询到全局作用域后获取 } foo(); }3. bar();
4 作用域的分类
4.1 全局作用域
最上层的作用域,javascript语言中编译最先开始工作的作用域。
4.2 函数作用域。
4.2.1 函数作用域的所在的作用域等级由函数定义所以决定。
let a="a" function foo(){ console.log(a);//a 在fun中执行时,foo作用域并不是在fun作用域的下级,所以这里输出a // console.log(b);//ReferenceError:报错 尽管在fun作用域内执行此方法,但是它的作用域与fun作用域属于同等级关系关系。 } function fun(){ let a="a_fun"; let b="b_fun"; function boo(){ console.log(a);//“a_fun” 由于boo作用域定义时处在fun作用域内,所以这里在boo作用域未查找到后,往上查fun作用域。 console.log(b);//"b_fun" } foo(); boo(); } fun();
4.2.2 函数的参数:函数作用域规则中的一部分。
1 function fun(a){ 2 console.log(a); 3 } 4 fun(5);
为了方便理解,以下是个人的理解。关于函数参数与该函数作用域的关系。
1 函数在定义时就创建声明了一个arguments的类数组对象,此时arguments类似一个空数组,当函数执行时将参数push进了arguments。
2 函数括号中的参数名称,首先在函数定义是将这些名称在函数中进行了变量声明,此时并没有赋值。当函数执行时这些参数变量会依次指向arguments类数组中的元素中指向的引用(类似双向绑定,也可以说两个指向同一个引用),函数中参数变量发生改变时arguments中相应元素值也会发生该改变(我觉得应该可以理解为参数名称和元素指向同一引用的值发生改变了)。
不过这里需要注意的时当函数定义时,给参数名称赋初始值后,函数内参数名称或相应arguments元素发生改变就不会一起改变了。参考变量C(这里理解是参数名称在函数定义时给初始值,造成函数执行时参数名称和arguments相应的元素指向的引用不是同一个了,所以在函数执行时只是将相应元素指向引用的值赋值给了函数名称指向引用的值。)
1 function fun(a,b,c=77) { 2 console.log("a:",a);//5 3 console.log("b",b);//6 4 console.log("c",c);//7 5 console.log("arguments:",arguments); //{ '0': 5, '1': 6 } 6 b=12 ;//var b=10;这里都一样 7 arguments[0]=10; 8 c=14; 9 arguments[2]=21; 10 console.log("b:",b); // 12 11 console.log("a:",a);//10 12 console.log("c:",c);//7 13 console.log("arguments:",arguments); //{ '0': 10, '1': 6, '2': 7 } 14 } 15 fun(5,6,7)
4.3 块作用域
在javascript中除了最常见的函数作用域单元外,还有很多块级作用域比如;if(){},for(){},with(){},try{}catch(){}等。