函数提升更改var值

时间:2018-02-28 06:10:45

标签: javascript

任何人都可以解释我为什么会得到不同的输出?

代码1:

var a = 1;

function b() {
  a = 10;
  console.log(a); //output 10
}

b();
console.log(a); //output 10

代码2:

var a = 1;

function b() {
  a = 10;
  console.log(a); //output 10
  function a() {}
}

b();
console.log(a); //output 1

为什么在调用函数“b”后我得到“a”变量的不同输出?需要一些明确的解释这里真正发生了什么?

2 个答案:

答案 0 :(得分:5)

我会说案例1非常明显,因为a会覆盖a=10的值。

在案例2中,由于function a() {}而且由于hoisting,变量声明和函数定义被移动到最接近的词法范围,function b()function a()所以a的范围限定为function b(),更改其值不会影响全局a的值,而是会覆盖function a()

由于全局值未更改,console.log(a)位于function b()以外的1正在记录lineReader.on('line', (creditCard: string) => { if (getAmexName(creditCard)) { outputAmex.push( `${getAmexName(creditCard)}: ${creditCard} (${ cardValidator(creditCard) ? 'valid' : 'invalid' })` ) } })

答案 1 :(得分:0)

我相信我们的OP尝试了互联网学习的权衡。信息的可访问性,但没有指南。不幸的是,我们不知道你知道多少,我们承担了很多事情,而那些你知道的事情很少,这些知识最终导致了解可怕的hoisting。这是典型的how magnets work,我会提供一些关键字,作为您理解JavaScript的指导。

Creation Stage + Activation aka Code execution Stage = Execution context

好的,但每个人都是什么?

Scope Chain + Creating arguments, functions, variables + value of 'this' keyword = Creation Stage

Assigning values and references to functions, then execute the code = Activation aka Code execution Stage

  • 创作阶段

    • 设置范围链
    • 创建包含变量的对象
      • 创建参数对象
      • 搜索函数声明及其中的每一个,在上面的对象上创建具有该名称的属性,如果存在函数名,则将覆盖ref指针值
      • 搜索变量声明及其中的每一个,在上面的对象中创建具有该名称的属性,同时将其值设置为undefined。如果名称已存在,则不执行任何操作。
    • this关键字的值已解决。
  • 执行阶段,即运行代码阶段

    • 执行exe ()函数并赋值to this = value is assigned

代码1 按照上述算法逐行开始执行。

第1阶段:创作阶段

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: `undefined`
  },
  this: //not important for this example
}

创作阶段已经完成。我们按照上面定义的说明逐行进行。现在我们处于执行阶段,分配=并执行()事件。我们再一次从第1行开始

第2阶段:执行阶段

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: 1 // because we did the `=` on line 1
  },
  this: //not important for this example
}

然后在第8行,我们发现函数执行意味着在全局执行上下文之上创建新的执行上下文,这意味着我们再次对该函数使用上面的算法。

第1阶段:创作阶段

bExeContext = {
  // This is the scope object, and in this object now is placed the global exe context we have worked on before
  scope: {globalExeContext}
  objForVariables = {
    //the is arguments object for this one but its empty, because we have no arguments for this function
    args:{},
    // function declarations are none here
    // variable declarations are none here
  },
  this: //not important for this example
}

现在,我们已进入执行阶段()=。在第1行,它被告知a = 10这意味着我们需要找到a来为其分配值。但是a对象中没有bExeContext?现在怎么办?现在我们进入scope并尝试在那里找到它。果然,我们的全球空间有a,现在我们为它分配10。我们已经覆盖了它。它永远不会是相同的,我将带回globalExeContext来向你展示。

第2阶段:执行阶段

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: 10 // look what you have done
  },
  this: //not important for this example
}

现在已解决此问题,我们返回执行b函数中的下一行,即console.log(a);。我们需要解决a并重新开始,b中的搜索没有给我们任何东西,但我们在globalExeStack中有它。你记得,它是10。所以我们记录10。我们已经到了函数的末尾,它从堆栈中弹出。它已不复存在。

现在我们继续执行全局代码的最后一行:console.log(a);并且很容易,globalExeStack中有a。它是10

代码2 第1阶段:创建阶段。

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: `undefined`
  },
  this: //not important for this example
}

第2阶段:执行阶段=() 第一行是相同的

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: 1
  },
  this: //not important for this example
}

和第8行告诉执行b函数。

第1阶段:创作阶段

bExeContext = {
  scope: {globalExeContext}
  objForVariables = {
    //the is arguments object for this one but its empty, because we have no arguments for this function
    args:{},
    // function declarations
    a: `points to function`
    // variable declarations are none here
  },
  this: //not important for this example
}

第2阶段:执行阶段

第1行告诉我找到a并为其指定10。我们很幸运,因为a可以在我们的bExeContext中找到。

bExeContext = {
  scope: {globalExeContext}
  objForVariables = {
    //the is arguments object for this one but its empty, because we have no arguments for this function
    args:{},
    // function declarations
    a: 10 // no longer points to `points to function`
    // variable declarations are none here
  },
  this: //not important for this example
}

这意味着我们无需前往全球空间查找a。执行b函数的第2行告诉console.log(a);。我们需要解决a是什么而且确定

bExeContext = {
  // blablbalblablbalblablblal     
    // blablbalblablbalblablblalb
    a: 10 // no longer points to `points to function`
    // blablbalblablbalblablblalb
  },
  // blablbalblablbalblablblalb
}

我们确实在bExeContext上找到了它。我们记录10。我们完成了b函数的执行,然后将它从执行堆栈中弹出。它不复存在了。

我们现在继续评估全球代码。我们完成了第9行的b();,现在我们在第10行。console.log(a);并要解决a我们必须先找到它。要刷新globalExeContext的外观,下面是最后一张图片:

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: 1
  },
  this: //not important for this example
}

果然,a1。我们记录1。我们完成了。

你看,只要你运行JS代码就可以实现hoisting。这是放下代码的过程,如果你愿意,可以将其排序。更准确地说,它是1.arguments 2.function声明3.variable声明。当您的代码只有几行或一行,或根本没有任何功能时,就会发生吊装,因为只要您尝试播放解释器的一部分并布置代码,就可以进行吊装。