问题1:这段JavaScript代码的输出是什么?
var a = 10;
function example() {
console.log(a);
var a = 20;
}
example();
输出: undefined
解释:
这段代码演示了JavaScript中一个常见的行为,称为变量提升。在JavaScript中,变量声明会被提升到其作用域的顶部,但赋值操作仍然保持在原位置。
当调用example()函数时,它首先尝试打印a的值。在函数内部,有一个var a = 20;语句,它在函数作用域内创建了一个新的局部变量a。然而,由于提升,var a;的声明被提升到函数的顶部,实际上使函数等同于:
function example() {
var a; // 提升的声明
console.log(a); // 输出 undefined
a = 20; // 赋值保持在原位置
}
问题2:输出是什么?
function trickyExample() {
console.log(a);
if (true) {
var a = 10;
}
console.log(a);
}
trickyExample();
输出:
o 第一个console.log:undefined
o 第二个console.log:10
解释:
在trickyExample()函数中,if块前后都有一个console.log(a)语句。在块内部,有一个var a = 10;声明。由于变量提升,var a;声明被提升到函数作用域的顶部,使其在整个函数中都可以访问。
然而,在JavaScript中,在块内用var声明的变量不是块作用域的;它们是函数作用域的。这意味着在if块内声明的变量a在整个trickyExample()函数中都可以访问,甚至在if块之前也可以。
当调用trickyExample()时,第一个console.log(a)语句将输出undefined,因为a被提升但还没有被赋值。第二个console.log(a)语句将输出10,因为a已经在if块内被赋值为10。
问题3:输出是什么?
function trickyFunction() {
if (true) {
var a = 5;
let b = 10;
}
console.log(a);
console.log(b);
}
trickyFunction();
输出:
o 第一个console.log:5
o 第二个console.log:ReferenceError
解释:
这个问题探讨了在JavaScript中块内用var和let声明的变量之间的差异。
在trickyFunction()中,有一个if块包含var a = 5;和let b = 10;声明。用var声明的变量是函数作用域的,并被提升到函数的顶部,而用let声明的变量是块作用域的,不会被提升。
当调用trickyFunction()时,var a被提升到函数的顶部,在整个函数中都可以访问。然而,let b是块作用域的,只在if块内可以访问。
第一个console.log(a)语句将输出5,因为var a由于提升而在整个trickyFunction()函数中都可以访问。
第二个console.log(b)语句将导致ReferenceError,因为let b被块作用域限制在if块内,在块外无法访问。
问题4:输出是什么?
var variable = 10;
(() => {
console.log(variable);
var variable = 20;
console.log(variable);
})();
输出:
o 第一个console.log:undefined
o 第二个console.log:20
解释:
在给定代码中,变量variable被声明并赋值为10。在立即调用函数表达式(IIFE)内部,同一个变量被用var重新声明并赋值为20。由于提升,第一个console.log(variable)打印undefined(变量被声明但还没有被赋值),第二个console.log(variable)打印20。这演示了JavaScript中提升和变量重新声明的影响。
问题5:输出是什么?
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
console.log(a);
输出: 1
解释:
代码以全局变量a设置为1开始。在函数b()内部,声明了一个局部函数a,它暂时遮蔽了全局a。b()将10赋给局部a,但这不会影响全局a。console.log(a)语句打印未改变的全局a,结果是输出1。
问题6:给定以下数据集,按年龄对学生进行分组。
const students = [
{ name: "Alice", age: 20 },
{ name: "Bob", age: 22 },
{ name: "Charlie", age: 19 },
{ name: "David", age: 22 },
{ name: "Eve", age: 20 },
];
解决方案:
// 解决方案1:
const result = Object.groupBy(students, ({age}) => age)
console.log(result)
// 解决方案2:
const result = Object.groupBy(students, student => student.age)
console.log(result)
输出:
{
19: [{ name: "Charlie", age: 19 }],
20: [
{ name: "Alice", age: 20 },
{ name: "Eve", age: 20 }
],
22: [
{ name: "Bob", age: 22 },
{ name: "David", age: 22 }
]
}
额外提示
在评论区中,有读者提到了另一个有趣的JavaScript陷阱:
var a = 339491.55;
var b = 678983.1;
alert(a + b);
小心浮点数精度问题! 这个例子展示了JavaScript中浮点数运算可能出现的精度问题。
关键概念总结
1. 变量提升(Hoisting):var声明的变量会被提升到作用域顶部
2. 作用域差异:var是函数作用域,let/const是块作用域
3. 立即调用函数表达式(IIFE):立即执行的匿名函数
4. 函数声明提升:函数声明也会被提升
5. Object.groupBy():ES2024新特性,用于数组分组