Skip to content

一些变量绑定的例子

下面就是一些可能会有混淆的例子,不过工作中一般用不上。

例子 1 连续赋值

js
let a = (b = {});
a.c = a = 1;
console.log(a, b); // 1, { c: 1 }

我们知道连续赋值是从右到左的,乍一看上面的例子可能会觉得不对。毕竟先执行 a = 1,然后 a.c = 1,但是 a 已经变成了数字 1,给 1 添加的 c 属性赋值是什么鬼?

实际上,赋值确实是从右到左的,但是要赋值的对象 a.c 在赋值之前就已经确定好了。上面的代码等价于:

js
let a = (b = {});
let temp = a; // 先把 a 的值保存下来
a = 1; // 然后把 a 赋值为 1
temp.c = 1; // 最后给原来的 a(也就是 temp) 添加 c 属性
console.log(a, b); // 1, { c: 1 }

例子 2 forEach

forEach 函数的作用很简单,遍历数组的每一项并执行回调函数,那假如回调函数中修改了数组呢?会发生什么?

js
let arr = [1, 2, 3];
arr.forEach((item, index) => {
  console.log(item);
  arr.push(item);
});
console.log(arr); // [1, 2, 3, 1, 2, 3]

上面代码中,在每次遍历时都往数组中添加了一个元素,最后会遍历几次?会发生死循环吗?

作为对比,我们把 forEach 换成 for...of

js
let arr = [1, 2, 3];
for (let item of arr) {
  console.log(item);
  arr.push(item);
}

可以看到,for...of 会一直遍历下去,因为它能够感知到数组的变化,而 forEach 则不会。forEach 在开始遍历时就确定了要遍历的元素个数,所以只会遍历三次。

自己简单实现一个 forEach

js
Array.prototype.myForEach = function (callback) {
  const len = this.length; // 先把长度保存下来
  // 可以看到,要遍历多少次是提前确定好的,后续数组的变化不会影响遍历次数
  for (let i = 0; i < len; i++) {
    callback(this[i], i, this);
  }
};

例子 3 ESM 导入

使用 ESM 模块化语法来导入导出时,导入的变量是只读的,不能被重新赋值。

下面代码中,foo.js 中导出了变量 a,在 main.js 中导入后尝试修改 a 的值,会报错:TypeError: Assignment to constant variable.

js
import { a } from "./foo.js";
a = 2; // TypeError: Assignment to constant variable.
js
export let a = 1;

而在 CommonJS 中,导入的变量是可以被重新赋值的:

js
let { a } = require("./foo.js");
a = 2; // 可以成功赋值
console.log(a); // 2
js
exports.a = 1;

Released under the MIT License