为什么在函数内部创建同名变量时函数参数没有被覆盖?
Why is the function argument not overwritten on creating variable of same name inside the function?
var a = 'why is this not undefined?';
function checkScope(a) {
var a;
console.log(a);
}
checkScope(a);
Javascript 是功能范围语言,对吧?当我在使用与函数参数相同名称的函数内部声明一个新变量时,为什么新定义的变量仍然保存与参数相同的数据?
我认为它应该是未定义的?
var a;
其实就是变量声明声明。定义函数时,其中声明的所有变量都在代码执行之前进行处理,因此您甚至可以在运行时实际声明行执行之前使用这些变量。这叫做var
hoisting。所以,不管你声明了多少次变量,这个变量实际上只声明了一次。
在您的例子中,您已将 a
定义为函数的参数之一,其作用域为当前函数。然后你声明了一个同名的变量。由于a
已经在函数中声明,作为参数之一,var a;
声明将被忽略。
这就是您在控制台中获得 why is this not undefined?
的原因。
假设您有 var a = 1;
而不是 var a;
,在这种情况下,变量已经声明,但 赋值表达式将在运行时计算 并且值 1
将分配给 a
。因此,console.log
将打印 1
.
此行为在 ECMA 脚本 5.1 规范中的 10.5 Declaration Binding Instantiation、
部分进行了解释
If code is function code, then
a. Let func be the function whose [[Call]] internal method initiated execution of code. Let names be the value of func’s
[[FormalParameters]] internal property.
b. Let argCount be the number of elements in args.
c. Let n be the number 0.
d. For each String argName in names, in list order do
i. Let n be the current value of n plus 1.
ii. If n is greater than argCount, let v be undefined otherwise let v be the value of the n’th element of args.
iii. Let argAlreadyDeclared be the result of calling env’s HasBinding concrete method passing argName as the argument.
iv. If argAlreadyDeclared is false, call env’s CreateMutableBinding concrete method passing argName as the
argument.
v. Call env’s SetMutableBinding concrete method passing argName, v, and strict as the arguments.
.....
For each VariableDeclaration and VariableDeclarationNoIn d in code, in source text order do
a. Let dn be the Identifier in d.
b. Let varAlreadyDeclared be the result of calling env’s HasBinding concrete method passing dn as the argument.
c. If varAlreadyDeclared is false, then
i. Call env’s CreateMutableBinding concrete method passing dn and configurableBindings as the arguments.
ii. Call env’s SetMutableBinding concrete method passing dn, undefined, and strict as the arguments.
正如我们在规范中看到的,函数中声明的参数和变量,实际上都是在定义它们的函数对应的执行环境中定义的。所以,如果参数和变量同名,变量实际上只定义一次,第二次声明被忽略。
您正在将变量 a 作为参数传递给函数检查。function checkScope(a) {
在您再次尝试声明 var a 的函数内,var a;
这两个在同一个范围内吧?即都在函数 check();
内
并且根据 docs,函数内部的 var a
是您作为参数传递的同一个变量,您只是在声明之前使用它...您可以使用 JS
所以你写的代码等同于
var a = 'why is this not undefined?';
function checkScope(a) {
console.log(a);
}
checkScope(a);
即变量 a 被忽略。
你期望 var a 应该 return undefined 那么代码就是这样
var a = 'why is this not undefined?';
function checkScope() {
var a
console.log(a);
}
checkScope();
这次我们没有将参数a传递给函数,而是在函数范围内创建了一个新变量var a
,因此变为undefined
因为JavaScript 忽略变量的重新声明。但是,如果你有这个:
var a = 'why is this not undefined?';
function checkScope(a) {
var a = 'foo';
console.log(a);
}
checkScope(a);
a
的值将被覆盖。因为var a = 'foo';
是由变量声明(var a;
)和赋值(a = 'foo';
)组成的;
此行为在 MDN docs 中进行了描述。
参数仍然在函数内部定义,因为当变量已经在同一范围内定义时 var a;
将被忽略。
像 var a;
这样的语句并不意味着变量是在代码中的那个点创建的。所有变量声明都是 hoisted 到范围的顶部,因此您可以在范围内多次重新声明它们,并且它们仍然只创建一次。
如果声明有赋值,例如 var a = 2;
,则赋值发生在代码中语句所在的位置,无论声明是否被忽略。
示例:
function check(a) {
// both a and b exist here
document.write('a = ' + a + ', b = ' + b + '<br>');
var b = 1;
document.write('a = ' + a + ', b = ' + b + '<br>');
var a = 2; // not recreated, but assigned
document.write('a = ' + a + ', b = ' + b + '<br>');
}
check(42);
var a = 'why is this not undefined?';
function checkScope(a) {
var a;
console.log(a);
}
checkScope(a);
Javascript 是功能范围语言,对吧?当我在使用与函数参数相同名称的函数内部声明一个新变量时,为什么新定义的变量仍然保存与参数相同的数据?
我认为它应该是未定义的?
var a;
其实就是变量声明声明。定义函数时,其中声明的所有变量都在代码执行之前进行处理,因此您甚至可以在运行时实际声明行执行之前使用这些变量。这叫做var
hoisting。所以,不管你声明了多少次变量,这个变量实际上只声明了一次。
在您的例子中,您已将 a
定义为函数的参数之一,其作用域为当前函数。然后你声明了一个同名的变量。由于a
已经在函数中声明,作为参数之一,var a;
声明将被忽略。
这就是您在控制台中获得 why is this not undefined?
的原因。
假设您有 var a = 1;
而不是 var a;
,在这种情况下,变量已经声明,但 赋值表达式将在运行时计算 并且值 1
将分配给 a
。因此,console.log
将打印 1
.
此行为在 ECMA 脚本 5.1 规范中的 10.5 Declaration Binding Instantiation、
部分进行了解释
If code is function code, then
a. Let func be the function whose [[Call]] internal method initiated execution of code. Let names be the value of func’s [[FormalParameters]] internal property.
b. Let argCount be the number of elements in args.
c. Let n be the number 0.
d. For each String argName in names, in list order do
i. Let n be the current value of n plus 1.
ii. If n is greater than argCount, let v be undefined otherwise let v be the value of the n’th element of args.
iii. Let argAlreadyDeclared be the result of calling env’s HasBinding concrete method passing argName as the argument.
iv. If argAlreadyDeclared is false, call env’s CreateMutableBinding concrete method passing argName as the argument.
v. Call env’s SetMutableBinding concrete method passing argName, v, and strict as the arguments.
.....
For each VariableDeclaration and VariableDeclarationNoIn d in code, in source text order do
a. Let dn be the Identifier in d.
b. Let varAlreadyDeclared be the result of calling env’s HasBinding concrete method passing dn as the argument.
c. If varAlreadyDeclared is false, then
i. Call env’s CreateMutableBinding concrete method passing dn and configurableBindings as the arguments.
ii. Call env’s SetMutableBinding concrete method passing dn, undefined, and strict as the arguments.
正如我们在规范中看到的,函数中声明的参数和变量,实际上都是在定义它们的函数对应的执行环境中定义的。所以,如果参数和变量同名,变量实际上只定义一次,第二次声明被忽略。
您正在将变量 a 作为参数传递给函数检查。
function checkScope(a) {
在您再次尝试声明 var a 的函数内,
var a;
这两个在同一个范围内吧?即都在函数 check();
内并且根据 docs,函数内部的 var a
是您作为参数传递的同一个变量,您只是在声明之前使用它...您可以使用 JS
所以你写的代码等同于
var a = 'why is this not undefined?';
function checkScope(a) {
console.log(a);
}
checkScope(a);
即变量 a 被忽略。
你期望 var a 应该 return undefined 那么代码就是这样
var a = 'why is this not undefined?';
function checkScope() {
var a
console.log(a);
}
checkScope();
这次我们没有将参数a传递给函数,而是在函数范围内创建了一个新变量var a
,因此变为undefined
因为JavaScript 忽略变量的重新声明。但是,如果你有这个:
var a = 'why is this not undefined?';
function checkScope(a) {
var a = 'foo';
console.log(a);
}
checkScope(a);
a
的值将被覆盖。因为var a = 'foo';
是由变量声明(var a;
)和赋值(a = 'foo';
)组成的;
此行为在 MDN docs 中进行了描述。
参数仍然在函数内部定义,因为当变量已经在同一范围内定义时 var a;
将被忽略。
像 var a;
这样的语句并不意味着变量是在代码中的那个点创建的。所有变量声明都是 hoisted 到范围的顶部,因此您可以在范围内多次重新声明它们,并且它们仍然只创建一次。
如果声明有赋值,例如 var a = 2;
,则赋值发生在代码中语句所在的位置,无论声明是否被忽略。
示例:
function check(a) {
// both a and b exist here
document.write('a = ' + a + ', b = ' + b + '<br>');
var b = 1;
document.write('a = ' + a + ', b = ' + b + '<br>');
var a = 2; // not recreated, but assigned
document.write('a = ' + a + ', b = ' + b + '<br>');
}
check(42);