美文网首页
js 变量提升(hoisting)

js 变量提升(hoisting)

作者: Johnson23 | 来源:发表于2019-07-30 16:57 被阅读0次

先了解一下变量的生命周期

  • 变量在它声明时初始化。
  • 局部变量在函数调用时创建,当函数执行完毕后销毁,这和为什么要使用闭包也有一点关系,当然这是另外的话题了。
  • 全局变量在页面(当前页面)关闭后销毁。

变量声明提升

  • 如果变量声明在函数里面,则将变量声明提升到函数的开头
  • 如果变量声明是一个全局变量,则将变量声明提升到全局作用域的开头
var a = 1
function test() {
  console.log(a)
  var a = 1
}
test()

根据变量声明提升和变量搜索机制,函数test应为

function test(){
  var a = undefined // 变量声明提升了,并初始化为undefined
  console.log(a)  // undefined
  a = 1 // 变量赋值
}

再来看看下面这个例子:

c = 5
function test2() {
    window.c = 3
    console.log(c)
    var c = 1
    console.log(window.c)
}
test3()

根据变量声明提升和变量搜索机制,函数test2应为

function test2() {
  var c = undefined // 局部变量c声明提升,并初始化为undefined
  window.c = 3
  console.log(c) // 此c为局部变量,undefined
  c = 1
  console.log(window.c) // 3
}

再来测试下const:

const d = 3
if(true) {
  console.log(d) // // Uncaught ReferenceError: x is not defined
  const d = 1
}

一开始我认为let和const也存在声明提升,看了知乎的这篇文章——我用了两个月的时间才理解 let之后改变了我的看法。以下摘自这篇文章的一部分。

首先明确一点:提升不是一个技术名词。

要搞清楚提升的本质,需要理解 JS 变量的「创建create、初始化initialize 和赋值assign」

有的地方把创建说成是声明(declare),为了将这个概念与变量声明区别开,我故意不使用声明这个字眼。

有的地方把初始化叫做绑定(binding),但我感觉这个词不如初始化形象。

我们来看看 var 声明的「创建、初始化和赋值」过程
假设有如下代码:

function fn(){
  var x = 1
  var y = 2
}
fn()

在执行 fn 时,会有以下过程(不完全):

  • 进入 fn,为 fn 创建一个环境。
  • 找到 fn 中所有用 var 声明的变量,在这个环境中「创建」这些变量(即 x 和 y)。
  • 将这些变量「初始化」为 undefined。
  • 开始执行代码
  • x = 1 将 x 变量「赋值」为 1
  • y = 2 将 y 变量「赋值」为 2

也就是说 var 声明会在代码执行之前就将「创建变量,并将其初始化为 undefined」。

这就解释了为什么在 var x = 1 之前 console.log(x) 会得到 undefined。

接下来来看 function 声明的「创建、初始化和赋值」过程
假设代码如下:

fn2()

function fn2(){
  console.log(2)
}

JS 引擎会有一下过程:

  • 找到所有用 function 声明的变量,在环境中「创建」这些变量。
  • 将这些变量「初始化」并「赋值」为 function(){ console.log(2) }。
  • 开始执行代码 fn2()

也就是说 function 声明会在代码执行之前就「创建、初始化并赋值」。

接下来看 let 声明的「创建、初始化和赋值」过程
假设代码如下:

{
  let x = 1
  x = 2
}

我们只看 {} 里面的过程:

  • 找到所有用 let 声明的变量,在环境中「创建」这些变量
  • 开始执行代码(注意现在还没有初始化)
  • 执行 x = 1,将 x 「初始化」为 1(这并不是一次赋值,如果代码是 let x,就将 x 初始化为 undefined)
  • 执行 x = 2,对 x 进行「赋值」

这就解释了为什么在 let x 之前使用 x 会报错:

let x = 'global'
{
  console.log(x) // Uncaught ReferenceError: x is not defined
  let x = 1
}

原因有两个

  • console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
  • 执行 log 时 x 还没「初始化」,所以不能使用(也就是所谓的暂时死区)

看到这里,你应该明白了 let 到底有没有提升:

  • let 的「创建」过程被提升了,但是初始化没有提升。
  • var 的「创建」和「初始化」都被提升了。
  • function 的「创建」「初始化」和「赋值」都被提升了。

最后看 const,其实 const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。

相关文章

  • 变量提升

    参考信息:https://www.runoob.com/js/js-hoisting.html 对变量提升的解释:...

  • js变量提升Hoisting

    最终结果为undefined这里有私有作用域的语法,加上变量提升,这段代码相当于: 额外内容提示:函数声明有函数声...

  • js 变量提升(hoisting)

    先了解一下变量的生命周期 变量在它声明时初始化。 局部变量在函数调用时创建,当函数执行完毕后销毁,这和为什么要使用...

  • 理解 JS 作用域链与执行上下文

    贫道,感觉,JS的坑,不是一般地大。 变量提升: 变量提升( hoisting )。 我可恨的 var 关键字: ...

  • 变量提升

    看下结果: 解释:JavaScript中的变量提升(Hoisting) 实际可以理解为: 涉及到 js的作用域(块...

  • 浏览器中的JavaScript执行机制

    变量提升:JavaScript代码是按顺序执行的吗? 变量提升(Hoisting)所谓的变量提升,是指在 Java...

  • 纪念js中的那些坑

    0X01 js 中不存在块级作用域 0X02 js中存在一个特性-变量提升(Hoisting),变量的声明都将会被...

  • 关于js的变量提升(hoisting)

    javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都...

  • 变量提升(hoisting)

    JavaScript的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的...

  • 变量提升(hoisting)

    变量提升(hoisting) 变量提升,提升的是声明而不是赋值所有的变量的声明语句,都会被提升到代码的头部,这就叫...

网友评论

      本文标题:js 变量提升(hoisting)

      本文链接:https://www.haomeiwen.com/subject/ollnrctx.html