几乎全部语言的最基础模型之一就是在变量中存储值,而且在稍后取出或修改这些值的能力。 做用域就是定义如何在某些位置存储变量,以及如何在稍后找到这些变量。javascript
编译的三个步骤java
1.分词/词法分析:将一连串的字符打断成有意义的片断,称为 token,例子:var a = 2;=> var, a, =, 2, ;
。闭包
2.解析:将一个 token 的流转换成一个嵌套元素的树,它综合地表示了程序的语法结构。这棵树称为抽象语法树(AST)函数
3.代码生成:这个处理将抽象语法树转换为可执行的代码。性能
JavaScript 的编译和其余语言不一样,不是提早发生在一个构建的步骤中。对于 JavaScript 来讲,在许多状况下,编译发生在代码执行前的仅仅几微秒以内。为了确保最快的性能,JS 引擎使用了 JIT等等优化
引擎:负责从始至终的编译和执行咱们的 JavaScript 程序。 编译器:引擎的朋友之一;处理全部解析和代码生成的活儿。 做用域:引擎的另外一个朋友;收集并维护一张全部被声明的标识符的列表,并对当前执行中的代码如何访问这些变量强制实施一组严格的规则。ui
对于 var a = 2;
这个语句,编译器将会这样处理:spa
1.遇到var a
,编译器让做用域去查看对于这个特定的做用域集合,变量 a 是否已经存在了。若是是,编译器就忽略这个声明并继续前进。不然,编译器就让做用域去为这个做用域集合声明一个称为 a 的新变量。code
2.而后编译器为引擎生成稍后要执行的代码,来处理赋值 a = 2
。引擎 运行的代码首先让做用域 去查看在当前的做用域集合中是否有一个称为 a 的变量能够访问。若是有,引擎就使用这个变量。若是没有,引擎会喊出一个错误。token
嵌套的做用域就像一个代码块儿或函数被嵌套在另外一个代码块儿或函数中同样,做用域被嵌套在其余的做用域中。
遍历嵌套做用域的简单规则:引擎从当前执行的做用域开始,在那里查找变量,若是没有找到,就向上走一级继续查找,如此类推。若是到了最外层的全局做用域,那么查找就会中止,不管它是否找到了变量。
做用域的工做方式有两种占统治地位的模型。其中第一种是最多见的词法做用域,另外一种是动态做用域。
JavaScript 所采用的做用域模型是词法做用域。
词法做用域是在词法分析时被定义的做用域。
欺骗词法做用域:eval 函数和 with 关键字的使用。
欺骗词法做用域的使用会致使更低下的性能。由于 JS 引擎的一些优化原理都归结在实质上在进行词法分析时能够静态地分析代码,并提早决定全部的变量和函数声明在什么位置。若是发现一个 eval 或是 with,它实质上就不得不假定本身知道的全部标识符的位置多是无效的。
函数中的做用域也就是声明的每个函数都为本身建立了一个做用域。 当即调用函数表达式能够生成一个本身的做用域。
ES6引入了 let 和 const,它们都会建立一个块儿做用域。 let 作出的声明不会在他们所出现的块儿的做用域中提高。
在代码的任何部分被执行以前,全部的声明,变量和函数,都会首先被处理。如下是两个例子
a = 2;
var a;
console.log( a ); // 2
复制代码
console.log( a ); // undefined
var a = 2;
复制代码
函数声明会被提高,可是函数表达式不会。
foo();
function foo() {
console.log( a ); // undefined
var a = 2;
}
复制代码
foo(); // 不是 ReferenceError, 而是 TypeError! 由于变量 foo 被提高了,可是值为 undefined
var foo = function bar() {
// ...
};
复制代码
函数声明和变量声明都会被提高。可是函数会首先被提高,而后才是变量。
foo(); // 1
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};
复制代码
这个代码被引擎解释执行为
function foo() {
console.log( 1 );
}
foo(); // 1
foo = function() {
console.log( 2 );
};
复制代码
这里的一个注意点就是 var foo 是一个重复的声明,会被忽略。
闭包就是函数可以记住并访问它的词法做用域,即便当这个函数在它的词法做用域以外执行时。 如下例子是一个典型的闭包案例
function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar;
}
var baz = foo();
baz(); // 2 -- 哇噢,看到闭包了,伙计。
复制代码
咱们生活中常用到的闭包
function wait(message) {
setTimeout( function timer(){
console.log( message );
}, 1000 );
}
wait( "Hello, closure!" );
复制代码
还有模块就是利用了闭包的力量,咱们看下面的代码
function CoolModule() {
var something = "cool";
var another = [1, 2, 3];
function doSomething() {
console.log( something );
}
function doAnother() {
console.log( another.join( " ! " ) );
}
return {
doSomething: doSomething,
doAnother: doAnother
};
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3
复制代码