【问题标题】:Function Hoisting changing the var value函数提升改变 var 值
【发布时间】:2018-02-28 06:10:45
【问题描述】:

谁能解释我为什么得到不同的输出?

代码 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”变量的输出不同?需要一些清楚的解释这里到底发生了什么?

【问题讨论】:

  • 我不确定你在这里不理解什么。你已经知道提升,甚至函数提升。
  • 我的第一个疑问!函数“a”会在这里执行吗?我什至没有调用该函数。调用函数 b 后如何影响输出?
  • @Nag 当你 a() 时,你将执行 a
  • 没错!这就是我的观点!那么在这两种情况下,答案都应该是 10,因为函数 a 没有被调用!完全不在场就好了,对吧?请让我理解,这可能很简单。
  • @Nag 看看第一个例子,你能告诉我a = 10; 是做什么的吗?

标签: javascript


【解决方案1】:

我会说案例 1 是不言自明的,因为 a 的值被 a=10 覆盖。

在情况 2 中,由于 function a() {}hoisting,变量声明和函数定义向上移动到最接近的词法范围,即 function b() for function a(),因此 a 的范围为 @987654328 @ 并更改其值不会影响全局 a 的值,而是覆盖 function a()

因为存在于function b() 之外的console.log(a) 正在记录1,因为全局值没有改变。

【讨论】:

  • 好吧,我知道函数 a() { } 向上移动了。类似这样的东西。变量 a = 1;函数 b() { 函数 a() {} a = 10;控制台.log(a); //输出 10 } b();控制台.log(a); //输出 1 现在为什么在调用函数 b 后插入具有相同 var 名称“a”的函数名称会影响输出?如果我删除函数“a”,输出将是 10,如预期的那样!那个空函数在这里做什么?它会被执行吗?我不这么认为
  • @Nag 是的。如果它纠正了疑问,请标记答案正确! :)
  • @Nag 我相信你写的那条评论根本上是错误的,你误解了提升的工作原理。代码不会上下移动,JS 只是在声明内容时具有优先权。首先是函数,然后是变量。
  • @void 在解释提升时请避免使用moving这个词。在creation 阶段没有任何动作。此外,a 函数根本不会被调用。 编辑原因:@user 错误
  • @Learnonhardway,如果你能一点一点地向我解释我的代码中发生了什么,那就太好了?究竟什么是吊装?我被这个概念困住了。
【解决方案2】:

我相信我们的 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

  • 创作阶段

    • 设置范围链
    • 创建将包含变量的对象
      • 创建参数对象
      • 搜索函数声明并为它们中的每一个在上面的对象上创建具有该名称的属性,如果函数名称存在,则引用指针值将被覆盖
      • 搜索变量声明并为它们中的每一个在上面的对象中创建具有该名称的属性,并将其值设置为undefined。如果名称已经存在,则什么也不做。
    • this 关键字的值已解析。
  • 执行阶段,也就是运行代码阶段

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

代码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 行开始

第二阶段:执行阶段

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 行,我们找到了函数执行,这意味着在全局执行上下文之上创建新的执行上下文,这意味着我们对该函数再次遵循上述算法。

第一阶段:创作阶段

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 来为其分配值。但是我们在bExeContext 对象中没有a?现在怎么办?现在我们进入scope 并尝试在那里找到它。果然,在我们的全局空间中有a,现在我们为它分配了10。我们已经覆盖了它。它永远不会是一样的,我会带回 globalExeContext 给你看。

第二阶段:执行阶段

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 函数。

第一阶段:创作阶段

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
}

第二阶段:执行阶段

第 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
}

果然a就是1。我们登录1。我们完成了。

你看,hoisting 只要你运行 JS 代码就实现了。这是放置代码位的过程,如果您愿意,可以将其整理出来。更准确地说是 1.arguments 2.function 声明 3.variable 声明。当您的代码只有几行或一行,或者根本没有任何功能时,就会发生提升,因为一旦您尝试扮演解释器的角色并布置代码,您就完成了提升。

【讨论】:

    猜你喜欢
    • 2014-02-01
    • 2021-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-25
    • 1970-01-01
    相关资源
    最近更新 更多