做用域是一套规则,用于肯定在何处以及如何查找变量。做用域查找会在找到第一个匹配的标识符时中止。
javascript
词法做用域
定义在词法阶段的做用域。词法做用域意味着做用域是由书写代码时函数声明的位置决定的。
编译的词法分析阶段基本可以知道所有标识符在哪里以及是怎么声明的,从而可以预测在执行过程当中如何对它们进行查找。java
JavaScript中有两个机制能够“欺骗”词法做用域:evel(...)和with。evel
能够对一段包含一个或多个声明的“代码”字符串进行演算,并借此来修改已经存在的词法做用域(在运行时)。with
本质上是经过将一个对象的引用看成做用域来处理,将对象的属性看成做用域中的标识符来处理,从而建立了一个新的词法做用域(在运行时)。函数
这两个机制的反作用是引擎没法在编译时对做用域查找进行优化,由于引擎只能谨慎地认为这样的优化是无效的。
使用这其中任何一个机制都将致使代码运行变慢。不要使用它们。
优化
函数做用域
函数做用域是指属于这个函数的所有变量均可以在整个函数的范围内使用及复用(事实上在嵌套的做用域中也可使用)。code
块级做用域
块做用域指的是变量和函数不只能够属于所处的做用域,也能够属于某个代码块(一般指{...}内部)with、try/catch、let、const
对象
变量的赋值操做会经历两个动做,首先编译器会在当前做用域中声明一个变量
(若是以前没有声明过),而后再运行时引擎会在做用域中查找变量
,若是可以找到就会对它赋值
,不然引擎就会抛出一个异常。ip
引擎会在解释javascript代码以前首先对其进行编译。编译阶段中的一部分工做就是找到全部的声明,并用合适的做用域将他们关联起来。每一个做用域都会进行提高操做
作用域
var a = 2; javascript实际上会将上述代码当作两个声明: var a; 和 a = 2; 第一个定义声明是在编译阶段进行,第二个赋值声明会被留在原地等待执行阶段。 eg1: a = 1; var a; console.log(a); //1 eg2: console.log(a); var a = 2; //undefined `只有声明自己会被提高,而赋值或其余运行逻辑会留在原地。`
function a(){ console.log(b) console.log(c) var b = 1 let c = 2 } a() 运行结果:undefined 和 ReferenceError foo() //不是ReferenceError而是TypeError bar() //ReferenceError var foo = function bar(){...} `函数声明会被提高,函数表达式却不会被提高` `即便是具名函数表达式,名称标识符在赋值以前也没法在所在做用域中使用`
foo() //3 function foo(){ console.log(1) } var foo = function(){ console..log(2) } function foo(){ console.log(3) } `函数声明和变量声明都会被提高,可是函数会首先被提高,而后才是变量`
ReferenceError
引用错误,不存在的变量被引用时发生的错误
TypeError
类型错误,存在变量,值的类型非预期类型时发生的错误
匿名函数
function(){...} 没有名称标识符
具名函数
function something(){...} 有名称标识符字符串
当即执行函数
函数被包含在一对()括号内部,所以成为了一个表达式,经过在末尾加上另外一个()能够当即执行这个函数,
例如:(function(){...})()
或(function(){...}())
编译器
文章摘取来源:《你不知道的JavaScript上卷》