JavaScript棘手代码第一部分

JavaScript代码系列(5部分)
JavaScript棘手代码第一部分
在JavaScript中处理设备方向
JavaScript棘手代码第二部分
JavaScript棘手代码第三部分
数组变异和非变异方法
问题1:这段JavaScript代码的输出是什么?
  1. var a = 10;
  2. function example() {
  3. console.log(a);
  4. var a = 20;
  5. }
  6. example();
javascript
输出: undefined
解释:
这段代码演示了JavaScript中一个常见的行为,称为变量提升。在JavaScript中,变量声明会被提升到其作用域的顶部,但赋值操作仍然保持在原位置。
当调用example()函数时,它首先尝试打印a的值。在函数内部,有一个var a = 20;语句,它在函数作用域内创建了一个新的局部变量a。然而,由于提升var a;的声明被提升到函数的顶部,实际上使函数等同于:
  1. function example() {
  2. var a; // 提升的声明
  3. console.log(a); // 输出 undefined
  4. a = 20; // 赋值保持在原位置
  5. }
javascript
问题2:输出是什么?
  1. function trickyExample() {
  2. console.log(a);
  3. if (true) {
  4. var a = 10;
  5. }
  6. console.log(a);
  7. }
  8. trickyExample();
javascript
输出:
第一个console.log:undefined
第二个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:输出是什么?
  1. function trickyFunction() {
  2. if (true) {
  3. var a = 5;
  4. let b = 10;
  5. }
  6. console.log(a);
  7. console.log(b);
  8. }
  9. trickyFunction();
javascript
输出:
第一个console.log:5
第二个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:输出是什么?
  1. var variable = 10;
  2. (() => {
  3. console.log(variable);
  4. var variable = 20;
  5. console.log(variable);
  6. })();
javascript
输出:
第一个console.log:undefined
第二个console.log:20
解释:
在给定代码中,变量variable被声明并赋值为10。在立即调用函数表达式(IIFE)内部,同一个变量被用var重新声明并赋值为20。由于提升,第一个console.log(variable)打印undefined(变量被声明但还没有被赋值),第二个console.log(variable)打印20。这演示了JavaScript中提升和变量重新声明的影响。
问题5:输出是什么?
  1. var a = 1;
  2. function b() {
  3. a = 10;
  4. return;
  5. function a() {}
  6. }
  7. b();
  8. console.log(a);
javascript
输出: 1
解释:
代码以全局变量a设置为1开始。在函数b()内部,声明了一个局部函数a,它暂时遮蔽了全局a。b()将10赋给局部a,但这不会影响全局a。console.log(a)语句打印未改变的全局a,结果是输出1。
问题6:给定以下数据集,按年龄对学生进行分组。
  1. const students = [
  2. { name: "Alice", age: 20 },
  3. { name: "Bob", age: 22 },
  4. { name: "Charlie", age: 19 },
  5. { name: "David", age: 22 },
  6. { name: "Eve", age: 20 },
  7. ];
javascript
解决方案:
  1. // 解决方案1:
  2. const result = Object.groupBy(students, ({age}) => age)
  3. console.log(result)

  4. // 解决方案2:
  5. const result = Object.groupBy(students, student => student.age)
  6. console.log(result)
javascript
输出:
  1. {
  2. 19: [{ name: "Charlie", age: 19 }],
  3. 20: [
  4. { name: "Alice", age: 20 },
  5. { name: "Eve", age: 20 }
  6. ],
  7. 22: [
  8. { name: "Bob", age: 22 },
  9. { name: "David", age: 22 }
  10. ]
  11. }
javascript
额外提示
在评论区中,有读者提到了另一个有趣的JavaScript陷阱:
  1. var a = 339491.55;
  2. var b = 678983.1;

  3. alert(a + b);
javascript
小心浮点数精度问题! 这个例子展示了JavaScript中浮点数运算可能出现的精度问题。
关键概念总结
变量提升(Hoisting):var声明的变量会被提升到作用域顶部
作用域差异:var是函数作用域,let/const是块作用域
立即调用函数表达式(IIFE):立即执行的匿名函数
函数声明提升:函数声明也会被提升
Object.groupBy():ES2024新特性,用于数组分组