浅谈解构赋值

88 阅读5分钟

解构赋值

什么是解构赋值

解构赋值是一种 Javascript 的语法特性,它可以让我们从数组或对象中提取数据,并赋值给变量,而不需要使用索引或属性名。

解构赋值的语法有两种形式:

  • 数组结构:var [a, b, c] = array;
  • 对象解构:var [x, y, z] = object;

数组解构按照数组元素的顺序,将对应位置的值赋值给变量。 对象解构按照对象属性的名称,将对应属性赋值给变量。

什么是可迭代对象

可迭代对象是指可以被遍历的对象,即可以用 for ... of 循环或 ... 扩展运算符来遍历其元素的对象。可迭代对象必须满足可迭代协议,即具有一个名为 Symbol.iterator 的属性,该属性是一个函数,返回一个可迭代对象

什么是 Symbol.iterator 属性和迭代器对象

Symbol.iterator 是一个内置的 Symbol 值,它表示一个对象的默认迭代器。 当使用 for ... of 循环或 ... 扩展运算符时,会自动调用该属性对应的函数,获取一个迭代器对象。迭代器对象是一个具有 next() 方法的对象,该方法返回一个包含 valuedone 两个属性的对象。

  • value 表示当前迭代的值
  • done 表示是否完成了迭代 每次调用 next() 方法,都会返回下一个迭代结果,直到 donetrue 为止,表示迭代结束。
var arr = [1, 2, 3];
const iter = arr[Symbol.iterator]();
console.log(iter); // Object [Array Iterator] {}
console.log(iter.next); // [Function: next]
console.log(iter.next()); // { value: 1, done: false }
console.log(iter.next()); // { value: 2, done: false }
console.log(iter.next()); // { value: 3, done: false }
console.log(iter.next()); // { value: undefined, done: true }

什么是 Object.values 方法和生成器函数

Object.values 方法是一个静态方法,它接受一个对象作为参数,返回一个包含该对象所有可枚举属性值的数组。

生成器函数是一种特殊的函数,它可以使用 function* 声明,并且可以使用 yield 关键字暂停执行并返回中间结果。

生成器函数返回一个生成器对象,它也是一个迭代器对象,可以使用 next() 方法继续执行函数,并获取下一个结果。

如何使用解构赋值

我们可以使用解构赋值来简化代码,提高可读性和效率。

例如:

// 不使用解构赋值
var arr = [1, 2, 3];
var a = arr[0];
var b = arr[1];
var c = arr[2];

// 使用解构赋值
var [a, b, c] = arr;
// 不使用解构赋值
var obj = {x: 10, y: 20, z: 30};
var x = obj.x;
var y = obj.y;
var z = obj.z;

// 使用解构赋值
var {x, y, z} = obj;

我们还可以使用解构赋值来交换变量的值,而不需要使用临时变量。

例如:

// 不使用解构赋值
var a = 1;
var b = 2;
var temp = a;
a = b;
b = temp;

// 使用解构赋值
var a = 1;
var b = 2;
[a, b] = [b, a];

我们还可以使用解构赋值来从函数返回多个值,而不需要使用数组或对象。

例如:

// 不使用解构赋值
function getCoords() {
  return [10, 20];
}

var coords = getCoords();
var x = coords[0];
var y = coords[1];

// 使用解构赋值
function getCoords() {
  return [10, 20];
}

var [x, y] = getCoords();

解构赋值面试题

面试题要求我们让下面的代码成立,不能更改此行代码:

var [a, b] = { a:1, b: 2 };

这意味着我们需要让右边的对象变成一个可迭代对象,即具有Symbol.iterator属性,返回一个迭代器对象。

解构并不要求右边是数组,只要右边是可迭代对象即可。

解构的本质

var [a, b] = arr;
// 第一步,调用arr[Symbol.iterator]()拿到迭代器iter
const iter = arr[Symbol.iterator]();

// 第二步,调用iter.next() 把得到的值赋值给a
var a = iter.next().value;
var b = iter.next().value;

解法一:在对象内部定义 Symbol.iterator 属性

我们可以在对象内部定义一个 Symbol.iterator 属性,该属性是一个函数,返回一个迭代器对象。

我们可以使用 Object.values 方法来获取对象的所有属性值,然后使用数组的 Symbol.iterator 方法来获取一个迭代器对象。

例如:

var [a, b] = {
	a: 1,
	b: 2,
	Symbol.iterator {
		var arr = Object.values(this);
		const iter = arrSymbol.iterator;
		return iter;
	}
}

console.log(a, b); // 1 2

解法二:在Object.prototype上定义 Symbol.iterator 属性

我们也可以在 Object.prototype 上定义一个 Symbol.iterator 属性,这样所有的对象都会继承这个属性,从而变成可迭代对象。

这种方法的优点是不需要改变对象的写法,但是缺点是会影响所有的对象,可能会造成意想不到的结果。

例如:

Object.prototype [Symbol.iterator] = function() {
	return Object.values(this)Symbol.iterator;
}
var [a, b] = {a: 1, b: 2};

console.log(a, b); // 1 2

解法三:使用生成器函数

我们还可以使用生成器函数来定义 Symbol.iterator 属性,这样可以更简洁地返回一个迭代器对象。

我们可以使用 yield* 关键字来委托给另一个可迭代对象,如 Object.values 方法返回的数组。

例如:

Object.prototype[Symbol.iterator] = function* () {
	return yield* Object.values(this);
}

var [a, b] = { a: 1, b: 2 };
console.log(a, b); // 1 2

上例中,定义了一个生成器函数作为 Object.prototype[Symbol.iterator] 的属性。这个生成器函数使用 yield* 语法将 Object.values(this) 的迭代器的值逐个生成出来,并作为生成器函数的返回值。这样一来,当使用对象解构赋值时,会调用对象的 Symbol.iterator 方法,该方法返回的生成器函数会在每次迭代时生成 Object.values(this) 的值,并将其赋值给相应的变量。