任何人都可以解释我为什么会得到不同的输出?
代码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”变量的不同输出?需要一些明确的解释这里真正发生了什么?
我相信我们的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
关键字的值已解决。()
并赋值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行开始
第二阶段:执行阶段
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来向您展示。
第二阶段:执行阶段
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
}
第二阶段:执行阶段=
和()
第一行是相同的
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
}
第二阶段:执行阶段
第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
。我们完了。
你知道,只要你运行JS代码就可以实现hoisting
。这是放下代码的过程,如果你愿意,可以将其排序。更准确地说,它是1.arguments 2.function声明3.variable声明。当您的代码只有几行或一行,或根本没有任何功能时,就会发生吊装,因为一旦您尝试播放解释器的一部分并布置代码,您就会完成吊装。
我会说案例1是不言自明的,因为a
的值被a=10
覆盖。
在案例2中,由于function a() {}
和hoisting
,变量声明和函数定义被移动到最接近的词汇范围,function b()
为function a()
所以a
的范围是function b()
并且更改其值不会影响全局a
的值,而是function a()
被覆盖。
因此,console.log(a)
以外的function b()
正在记录1
,因为全球价值没有变化。