你不知道的javascript(1)

1、作用域

1.1 编译原理

在传统编译语言的流程中,程序的一段源码在执行之前会经历三个步骤,统称为“编译”。

  • 分词/词法分析(Tokenizing/Lexing)
  • 解析/语法分析器
  • 代码生成

比起这些编译过程只有三个步骤的语言的编译器,Javascript引擎要复杂得多。例如,在语法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。

首先,JavaScript 引擎不会有大量的(像其他语言编译器那么多的)时间用来进行优化,因
为与其他语言不同,JavaScript 的编译过程不是发生在构建之前的。

对于JavaScript 来说,大部分情况下编译发生在代码执行前的几微秒(甚至更短!)的时
间内。在我们所要讨论的作用域背后,JavaScript 引擎用尽了各种办法(比如JIT,可以延
迟编译甚至实施重编译)来保证性能最佳。

简单地说,任何JavaScript 代码片段在执行前都要进行编译(通常就在执行前)。因此,
JavaScript 编译器首先会对var a = 2; 这段程序进行编译,然后做好执行它的准备,并且
通常马上就会执行它。

1.2 理解作用域

1、名词解释
  • 引擎
    从头到尾负责整个javascript程序的编译及执行过程
  • 编译器
    引擎的好朋友之一,负责语法分析及代码生成等。
  • 作用域
    引擎的另一位好朋友,负责手机并维护有所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。

1.4 异常

如果RHS查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出ReferenceError异常,因为在任何相关的作用域中都无法找到它。
如果RHS查询找到了一个变量,但是你尝试对这个变量的值进行不合理的操作,比如试图对一个非函数类型的值进行函数调用,或者引用Null或者undefined类型的值中的属性,那么引擎会抛出另外一种异常,叫做TypeError。ReferenceError同作用域判别失败相关,而TypeError则代表作用域判别成功了,但是对结果的操作是不合法的。

2、提升

包括变量和函数在内的所有声明都会在任何代码执行前首先被处理。也就是声明提升。
函数跟变量声明都会被提升。其中函数会首先被提升,其次才是变量。

foo(); //1
var foo;
function foo(){
  console.log(1);
}

foo = function() {
  console.log(2);
}

会输出1而不是2.这段代码会被引擎理解为如下形式:

function foo(){
  console.log(1);
}
foo();//1
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);
}

ps:声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会被提升。

猜你喜欢

转载自blog.csdn.net/weixin_34203426/article/details/87639544