资讯专栏INFORMATION COLUMN

(深究)声明提前(Hoisting)

陈伟 / 1036人阅读

摘要:有意思的是,这意味着变量在声明之前甚至已经可用。的这个特性被非正式地称为声明提前,即函数里声明的所有变量但不涉及赋值都被提前至函数体的顶部。但实际上会将其看成两个声明和。第二个赋值声明会被留在原地等待执行阶段。

简介

JavaScript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的。有意思
的是,这意味着变量在声明之前甚至已经可用。JavaScript的这个特性被非正式地称为
声明提前(hoisting) ,即JavaScript函数里声明的所有变量(但不涉及赋值)都被“提
前”至函数体的顶部。如果对提升不太明白的,请参考JavaScript高级程序设计177页函数表达式.MDN变量提升

示例
函数声明:

sayHi(); //不会报错,在执行代码之前会先读取函数声明,也就是 函数声明提升

function sayHi(){

  alert("Hi")

}

通过提升其实是这样的:

//函数声明提升到顶部

function sayHi(){

  alert("Hi")

}

sayHi(); //不会报错

函数表达式:

sayHi(); // 报错  

var sayHi = function(){

  alert("Hi")

}
函数表达式其实是创建一个匿名函数然后赋值给变量,通过提升应该是这样的:

//函数声明和变量声明都被提升到作用域顶部,函数优先

functionsayHi(){

  alert("Hi")

}

//变量声明被提升到顶部

var sayHi;

sayHi(); // 报错  

//变量赋值被留在原地

sayHi = function(){

  alert("Hi")

}
函数提升优先级高于变量提升
a()  // alert(1)
var a = 1
function a(){
  alert(1)
}

通过提升:
function a(){
  alert(1)
}
var a 
a()
a = 1
提问 为什么会有提升

比起那些编译过程只有三个步骤的语言的编译器,JavaScript引擎要复杂得多。例如,在语法分析 和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。我们通过编译器在词法阶段进行词法分析,生成词法作用域,词法作用域就是用来管理引擎如何在当前作用域以及嵌套 的子作用域中根据标识符名称进行变量查找。提升的作用使得所有声明都在词法作用域的上方,这样引擎在作用域及嵌套作用域中变量查找可以更快更简单。

提升的机制

引擎会在解释 JavaScript代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适 的作用域将它们关联起来,这也正是词法作用域的核心内容。
因此,正确的思考思路是,包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。
当你看到 var a = 2; 时,可能会认为这是一个声明。但JavaScript实际上会将其看成两个声 明: var a; 和 a = 2; 。第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在原地等待执 行阶段。

let究竟有没有提升

MDN_let及暂存死区

var i = 10;
function a(){
   i = 3;
   var i = 1;  //因为 var 提升了 所以在 function里 创建了 局部变量 i
}
console.log(i) // 10
let a = 1
{
  a = 2;
  let a  = 3;   //报错
}

//这里的报错,我认为是规范化强制性报错,并不代表let没有提升
//我的猜想如下,

let a = 1
{
  let a // 暂存死区 开始的地方就是这里
  a = 2 // 由于 a = 2 在 暂存死区 中,所以报错
  a // 暂存死区 结束的地方就是这里
}
为什么let要强制性报错

不推荐使用eval及with等,使用let/const 代替var,使用块级作用域
目的就是为了,减少词法欺骗以及作用域的混乱导致的性能问题,使得javascript引擎运行更快。
使用let 就要按照let 的使用规定,不然和使用var又没什么区别了,这是我的想法。

为什么函数比变量提升优先级更高

未完待续...
欢迎补充批评...

参考文章:
知乎提问
strack overflow
JavaScript引擎解析预编译
我的博客园
《你不知道的JavaScript 上》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95715.html

相关文章

  • javascript 变量提升(Hoisting

    摘要:简介变量提升意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。 简介 变量提升意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。 实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。 声明变量的方法 var、let、const 不用以上关键字...

    TwIStOy 评论0 收藏0
  • JavaScript-预编译

    摘要:预编译发生在函数执行前也就是说函数执行时,预编译已经结束。五总结理解预编译需要明白变量函数声明和变量赋值。预编译阶段,只进行变量函数声明,不会进行变量的初始化即变量赋值,所有变量的值都是变量赋值是在解释执行阶段才进行的。 一、JS的概念 JavaScript ( JS ) 是一种具有函数优先的轻量级解释型或即时编译型的编程语言。 二、JS语言特点 2.1 单线程 (1)JavaScri...

    Aldous 评论0 收藏0
  • 理解 JavaScript(二)

    摘要:所以形式参数是本地的,不是外部的或者全局的。这叫做函数声明,函数声明会连通命名和函数体一起被提升至作用域顶部。这叫做函数表达式,函数表达式只有命名会被提升,定义的函数体则不会。 Scoping & Hoisting var a = 1; function foo() { if (!a) { var a = 2; } alert(a); }; ...

    luxixing 评论0 收藏0
  • JS基础篇--函数声明与定义,作用域,函数声明与表达式的区别

    摘要:在中,有四种方式可以让命名进入到作用域中按优先级语言定义的命名比如或者,它们在所有作用域内都有效且优先级最高,所以在任何地方你都不能把变量命名为之类的,这样是没有意义的形式参数函数定义时声明的形式参数会作为变量被至该函数的作用域内。 Scoping & Hoisting 例: var a = 1; function foo() { if (!a) { var ...

    TerryCai 评论0 收藏0
  • JS 作用域链

    摘要:首先,在创建函数时,作用域链内就会先填入对象,图片只例举了全部变量中的一部分。然后,解释器进入函数的执行环境,同样的,首先填入父级的作用域链,就是的,包括了对象活动对象。之后再把的活动对象填入到作用域链最顶部,这就是的作用域链了。 之前学习JS函数部分时,提到了作用域这一节,但是因为使用材料书不同,今天在读博客的时候发现其实还有一个知识点即作用域链,所以来写一些个人理解和认识加深记忆。...

    darry 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<