一、编译原理javascript
分词/词法分析( Tokenizing/Lexing)java
这个过程会将由字符组成的字符串分解成( 对编程语言来讲) 有意义的代码块, 这些代码块被称为词法单元( token)。 例如, 考虑程序 var a = 2;。 这段程序一般会被分解成为下面这些词法单元: var、 a、 =、 2 、 ;。 空格是否会被看成词法单元, 取决于空格在这门语言中是否具备意义。git
二、理解做用域github
当你看到var a=2;
这个代码段的时候,你也许只会认为这是一个声明语句,可是事实上,浏览器引擎并不会这么认为!其实浏览器会这么认为:编程
一、遇到 var a, 编译器会询问做用域是否已经有一个该名称的变量存在于同一个做用域的集合中。 若是是, 编译器会忽略该声明, 继续进行编译; 不然它会要求做用域在当前做用域的集合中声明一个新的变量, 并命名为 a。浏览器
二、接下来编译器会为引擎生成运行时所需的代码, 这些代码被用来处理 a = 2 这个赋值操做。 引擎运行时会首先询问做用域, 在当前的做用域集合中是否存在一个叫做 a 的变量。 若是是, 引擎就会使用这个变量; 若是否, 引擎会继续查找该变量。编程语言
三、做用域嵌套函数
当一个块或函数嵌套在另外一个块或函数中时, 就发生了做用域的嵌套。 所以, 在当前做用域中没法找到某个变量时, 引擎就会在外层嵌套的做用域中继续查找, 直到找到该变量,或抵达最外层的做用域( 也就是全局做用域) 为止。ui
function foo(a) {
console.log(a + b);
}
var b = 3;
foo(3);复制代码
遍历嵌套做用域链的规则很简单: 引擎从当前的执行做用域开始查找变量, 若是找不到,就向上一级继续查找。 当抵达最外层的全局做用域时, 不管找到仍是没找到, 查找过程都会中止。spa
四、当即执行函数
var a = 3;
(function IIFE() {
var a = 4;
console.log(a);
})();
console.log(a);复制代码
因为函数被包含在一对 ( ) 括号内部, 所以成为了一个表达式, 经过在末尾加上另一个( ) 能够当即执行这个函数, 好比 (function foo(){ .. })()。 第一个 ( ) 将函数变成表达式, 第二个 ( ) 执行了这个函数。
相较于传统的 IIFE 形式, 不少人都更喜欢另外一个改进的形式: (function(){ .. }())。 仔
细观察其中的区别。 第一种形式中函数表达式被包含在 ( ) 中, 而后在后面用另外一个 () 括号来调用。 第二种形式中用来调用的 () 括号被移进了用来包装的 ( ) 括号中。这两种形式在功能上是一致的。 选择哪一个全凭我的喜爱。
提高
考虑如下代码:
a=2;
var a;
console.log(a);复制代码
你认为 console.log(..) 声明会输出什么呢?
不少开发者会认为是 undefined, 由于 var a 声明在 a = 2 以后, 他们天然而然地认为变量被从新赋值了, 所以会被赋予默认值 undefined。 可是, 真正的输出结果是 2。
考虑另一段代码:
console.log(a);
var a=2;复制代码
鉴于上一个代码片断所表现出来的某种非自上而下的行为特色, 你可能会认为这个代码片断也会有一样的行为而输出 2。 还有人可能会认为, 因为变量 a 在使用前没有先进行声明,所以会抛出 ReferenceError 异常。
不幸的是两种猜想都是不对的。 输出来的会是 undefined。
那么到底发生了什么? 看起来咱们面对的是一个先有鸡仍是先有蛋的问题。 究竟是声明( 蛋) 在前, 仍是赋值( 鸡) 在前?
正确的思考思路是, 包括变量和函数在内的全部声明都会在任何代码被执行前首先被处理。当你看到 var a = 2; 时, 可能会认为这是一个声明。 但 JavaScript 实际上会将其当作两个声明: var a; 和 a = 2;。 第一个定义声明是在编译阶段进行的。 第二个赋值声明会被留在原地等待执行阶段。
咱们的第一个代码片断会以以下形式进行处理:
var a;
a = 2;
console.log( a );复制代码
其中第一部分是编译, 而第二部分是执行。
相似地, 咱们的第二个代码片断实际是按照如下流程处理的:
var a;
console.log( a );
a = 2;复制代码
所以, 打个比方, 这个过程就好像变量和函数声明从它们在代码中出现的位置被“ 移动”到了最上面。 这个过程就叫做提高。换句话说, 先有蛋( 声明) 后有鸡( 赋值)。
只有声明自己会被提高, 而赋值或其余运行逻辑会留在原地。 若是提高改变了代码执行的顺序, 会形成很是严重的破坏。
五、函数优先
函数声明和变量声明都会被提高。 可是一个值得注意的细节( 这个细节能够出如今有多个“ 重复” 声明的代码中) 是函数会首先被提高, 而后才是变量。
考虑如下代码:
foo();
var foo;
function foo() {
console.log(1);
}
foo = function () {
console.log(2);
};复制代码
会输出 1 而不是 2 ! 这个代码片断会被引擎理解为以下形式:
function foo() {
console.log(1);
}
foo();
foo = function () {
console.log(2);
};复制代码
注意, var foo 尽管出如今 function foo()... 的声明以前, 但它是重复的声明( 所以被忽略了), 由于函数声明会被提高到普通变量以前。
尽管重复的 var 声明会被忽略掉, 但出如今后面的函数声明仍是能够覆盖前面的。
foo();//3
function foo() {
console.log(1);
}
var foo = function () {
console.log(2);
};
function foo() {
console.log(3);
}复制代码
欢迎关注个人GiHutb:github.com/HuangQinJia…
我的博客:blog.csdn.net/sinat_35512…
欢迎扫描下方二维码关注个人GtHub以及我的博客