JS笔记(一):声明提升

我们习惯将 var a = 2; 看作一个声明,而实际上JavaScript引擎并不这么认为。他将 var a a = 2 当作两个单独的声明,第一个是编译阶段的任务,第二个则是执行阶段的任务。

——《你不知道的Js》

 

变量提升

变量提升的概念已经为大家所熟知,简单来讲就是在代码执行前编译器会将变量的声明提升至其所在作用域(不是全局作用域)的顶端。看一个简单的例子。

         

如果不存在变量提升,很显然这行代码会给我们抛出一行错误  ReferenceError : a is not defined . 因为我们还没有定义 a 就使用了它。

由于存在变量声明提升,通过预编译对 a 的声明已经提升至最前,于是这里打印变量 a 的值就不会抛出 ReferenceError ( a is not defined )异常。而是打印出了 a 的值 undefined

为什么 a 为 undefined ? 这过程中还有一些细节需要注意。比如赋值与声明提升的先后关系。

undefined 表明 a 处于未赋值状态。说明了变量的赋值操作并未随着变量声明的提升而被提升,而是仍被留在原地。等同于下图。

 

函数声明提升

跟变量声明的提升一样,只不过声明的不再是变量而是一个函数了 var a  = >  function a(){...} 。还是来看例子。

由于存在函数声明提升,函数 foo 被正常执行打印出 a 的值为2。但这里同样有一个细节需要注意。

这里看到,函数声明会被提升,但函数表达式并不会。其中的道理跟上面的变量赋值是一样的。对变量 foo 的声明首先被提升,将一个匿名函数赋值给变量 foo 的操作还被丢在原地。像下面这样。

执行到 foo() 时,这里的 foo 只是一个值为 undefined 的小变量,还未被改变为函数呢。对一个 undefined 进行函数调用当然要抛出 TypeError 错误。

说到这里,还有一种具名的函数表达式(非匿名函数表达式),常常被人(我)误解。看例子。

为什么会出现 ReferenceError ? 先从函数的声明方式说起。函数有且只有两种声明方式。

  1.  一般函数声明:function foo(){ }
  2. 函数表达式声明:var foo = function(){ }; 等号后面必须为匿名函数

 对于 var foo = function bar(){ }; 这种具名函数表达式声明函数的方式等同于普通函数表达式。但此时的 bar 仅仅作为 foo() 的一个内部属性,这就是上例中抛出 ReferenceError 的原因。

bar 作为 foo() 的内部属性只可以在 foo() 内部访问,于是上图例中返回了 bar() 函数。 在函数外部调用局部变量 bar 就一定会出现 ReferenceError 错误。

 

优先级问题

 一般存在两个优先级问题:

  1. 函数的声明提升优先级高于同名变量声明提升。

      >>>  

  由此可见,无论在什么时候函数声明总是会被提升到普通变量之前。另外,重复的 var 声明会被忽略(严格模式下,let  const 不可重复声明,抛出 SyntaxError ),但出现在后面重复的函数声明会覆盖前面的。

  2. 函数形参高于同名局部变量。

  之前我还从没注意过这个问题,直到昨天做了一道题qwq。我先来简化一下原题便于理解他们的优先级问题。

      >>>   

  函数内部局部变量 foo 声明提升到所在作用域顶部后,此时它的值应该为 undefined 。像这样。

  

  但打印 foo 值却显示为1。说明此时打印 foo 实际上是函数的形参,也同时说明了函数的形参是优先于函数局部同名变量的。

  (形参实际上是函数内部自动创建的新变量,这样就相当于两个重复声明后面的声明被忽略,优先级关系也就不难理解了)

  接下来上原题啦,其实很简单。不多做解释。但如果不清楚他们的优先级关系就会做错喔。

   

  最后,重复命名可能会导致一些我们难以掌控的事情发生,所以尽量不要在同一作用域中进行重复定义。

  --End--

posted @ 2017-06-22 11:28  tt273z  阅读(201)  评论(0编辑  收藏  举报