作用域
- 引擎
从头到尾负责整个JavaScript程序的编译及执行过程 - 编译器
负责语法分析及代码生成(拆分语法) - 作用域
负责收集并维护由所声明的标识符(变量)组成的一系列查询,并实施一套严格的规则,确定当前执行的代码对这些标识符的访问权限
作用:负责存储变量
使用步骤
变量的赋值操作会执行两个动作:
-
首先编译器会在当前作用域中声明一个变量(如果之前没有声明过,会声明;如果有会忽略这个声明)
-
运行时,引擎会在当前作用域中查找该变量,如果找到就会对它赋值,否则,抛出异常
遍历嵌套作用域链的规则:引擎从当前的执行作用域开始查找变量,如果找不到会向上一级继续查找。当抵达最外层的全局作用域时,无论找没找到都会停止这个过程。
作用域查找会在找到第一个匹配的标识符时停止。因此可以在多层嵌套的作用域中定义同名的标识符,这叫做“遮蔽效应”。
总结为:作用域总是向上查找对应的变量,但不能向下访问。
function foo(){
function bar(a){
i = 3;
console.log( a + i );
}
for( var i=0; i<10; i++){
bar( i*2);
}
}
foo();
上述代码会造成死循环。因为bar函数里面的i=3,会覆盖掉外层的foo函数里面i的赋值,造成 i<10 这个条件无法满足,循环不会停止。
无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定
避免变量冲突
- 函数表达式
(function foo(){
... //foo只能在这里被访问,无法再外部作用域中被访问,不会污染了外部作用域。
})();
区分函数表达式和函数声明
区分两者最简单的方法就是看function关键字出现在函数声明中的位置,如果是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
- 块作用域
JS引擎
- JS引擎是单线程的
如果JS引擎是多线程的,现在有两个线程process1和process2,它们可以同时对一个DOM进行操作,比如process1删除了该DOM,而process2需要编辑该DOM,那么浏览器究竟该如何执行,这就存在很大的问题。所以JS引擎不能设置为多线程。 - JS为什么需要异步
如果JS中没有异步,有些操作(循环,DOM操作)需要花费很长的时间,就会被堵塞,影响了之后程序的执行,因此需要异步。 - 单线程如何实现异步
依靠浏览器的时间循环机制
进程和线程:一个程序至少有一个进程,一个进程至少有一个线程。
编译器
编译器工作的时候会分为两部分,编译阶段和执行阶段。
- 编译阶段:变量提升
- 执行阶段:赋值或其他运行逻辑会被留在原地
变量提升
- 函数声明会被提升,函数表达式不会
- 提升的时候函数优先,即首先会提升函数,然后再是变量。(注意:如果函数变量已经声明过了,变量再进行声明会被忽略,不能重复声明)