数组的解构赋值:

基本用法

  • 按照一定模式,从数组和对象中提取对象,对变量进行赋值,称为解构,写法属于“模式匹配”:按照对应位置,对变量赋值
let [a,b,c] = [1,2,3]

let [head,...tail] = [1,2,3,4]
head = 1
tail = [2,3,4]
  • 解构不成功:变量的值就等于undefined.
let [bar,foo] = [1]
foo = undefined
  • 不完全解构:
let [a,[b],d] = [1,[2,3],4]
a = 1
b=  2
d = 4
  • 如果右边不是数组(不是可遍历的结构,参见《Iterator》一章),会报错
//转为对象以后不具备 Iterator 接口
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
//本身就不具备 Iterator 接口
let [foo] = {};
  • set结构,也可以使用数组的解构赋值
let [x,y,z] = new Set(['a','b','c'])
  • 只要数据结构具有迭代,都可以解构赋值

Generator函数

默认值

  • 当一个数组成员严格等于(===)undefined的时候,才采用默认值([y= 'b'])
let [foo = true] = []
foo = true

let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
  • 默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
function f() {
  console.log('aaa');
}
let [x = f()] = [1];
  • 默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined

对象的解构赋值

简介

  • 与数组不同的是,对象的解构没有次序,必须与属性同名,才能取值
let {foo,bar} = {foo:'aaa',bar:'bbb'}
foo  ='aaa'
bar = 'bbb'
  • 解构失败,变量的值为undefined
let { bar, foo, baz } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
baz //undefined
  • 可以将现有对象的方法,赋值到某个变量
// 例一:将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上
let {log,sin,cos} = Math
// 例二:将console.log赋值到log变量
const {log} = console
log('hello') = hello
  • 变量名与属性名不一致:真正被赋值的是后者,而不是前者
let {foo:baz} = {foo:'aaa',bar:'bbb'}
baz:'aaa'
foo // error: foo is not defined

let obj = {first:'hello',last:'world'}
let {first:f,last:l} = obj
f = hello
l:world

let obj = {p:["hello",{y:"world"}]}

let {p:[x,{y}]} = obj
x = 'hello'
y = "world"

let {p,p:[x,{y}]} = obj
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
  • 三次解构赋值:
const node = {
  loc: {
    start: {
      line: 1,
      column: 5
    }
  }
};
let { loc, loc: { start }, loc: { start: { line }} } = node;

line // 1
loc  // Object {start: Object}
start // Object {line: 1, column: 5}

//只有line是变量,loc和start都是模式,不是变量
  • 嵌套赋值:
let obj = {}
let arr = []
({foo:obj.prop,bar:arr[0]} = {foo:123,bar:true})

obj // {prop:123}
arr // [true]
let {foo:{bar}} = {baz:'baz'} //报错(因为foo这时等于undefined再取子属性就会报错)
  • 对象的解构赋值可以取到继承的属性:
const obj1= {}
const obj2 = {foo:'bar'}
Object.setPrototypeof(obj1,obj2)
const {foo} = obj1
foo = 'bar'

对象obj1的原型对象是obj2。
foo属性不是obj1自身的属性,而是继承自obj2的属性,解构赋值可以取到这个属性。
  • setPrototypeof:
__proto__原来获取或设置当前对象的prototype(原型)对象。
因为__proto__是一个内部属性,不是一个正式的对外的API.
所以在操作原型对象时应该使用Object.getPrototypeOf()代替读取操作,使用Object.setPrototypeOf()代替设置操作。
setPrototypeof:设置一个指定的对象的原型(即内部[[Prototype]]属性到另一个对象或null
Object.setPrototypeOf(obj, prototype)
obj:将被设置原型的对象.
prototype:该对象新的原型(可以是一个对象或者null)

默认值

var {x:y = 5} = {}
y = 5
var {x:y = 3} = {x = 5}
y = 5
  • 默认值生效的条件是,对象的属性值严格等于undefined
var {x = 3} = {x = undefined}
x = 3
var {x = 3} = {x = null}
x = null
  • 对数组进行对象属性的解构(属性名表达式):
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
  • 错误的写法:
let x;
{x} = {x: 1};   // SyntaxError: syntax error
会报错,因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。
只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。

// 正确的写法
let x;
({x} = {x: 1});

字符串的解构赋值

  • 字符串被转换成了一个类似数组的对象
const [a,b,c,d,e] = "hello"
a = h
b = e
c = l
d = l
e = o
  • 类似数组都有一个length属性,可以对这个属性解构赋值
let {length : len} = 'hello';
len // 5

数值和布尔值的解构赋值

  • 解构赋值的规则:

只要等号右边的值不是数组或对象,就先转化为对象.

  • 因为数值和布尔值的包装对象都有toString属性,因此变量s都能取到值:
let {toString:s} = 123
s === Number.prototype.toString

let {toString:s} = true
s === Boolean.prototype.toString
  • 由于undefined和null无法转化为对象,所以解构会报错
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

函数参数的解构赋值

function add([x,y]){
    return x+y
}
add([1,2])  //3

[[1,2],[3,4]].map(([a,b])=>{a+b})  //[3,7]
map:遍历数组返回一个新数组

对象的解构赋值默认值,没有使用函数参数的默认值:
解释

function foo({ x, y = 0 }) {
    console.log(x, y)
}
foo({}); // undefined 0
foo({ x: 0 }); // 0 0
foo({ x: 1, y: 1 }); // 1 1
foo(); //报错:TypeError: Cannot read property 'x' of undefined
如果函数foo调用时没提供参数,变量x和y不会生成,从而报错。

function foo({ x, y = 0 } = {}) {
    console.log(x, y)
}
foo({}); // undefined 0
foo({ x: 0 }); // 0 0
foo({ x: 1, y: 1 }); // 1 1
foo(); // undefined 0
为函数参数变量x、y指定默认值
function move({x= 0,y = 0} = {}){
    return [x,y]
}
move({x:3,y:8}) = [3,8]
move({x :3}) = [3,0]
move({}) = [0,0]
move() = [0,0]

为函数参数指定默认值{0, 0}
function move({x,y} = {x : 0,y : 0}){
    return [x,y]
}
move({x:3,y:8}) = [3,8]
move({x :3}) = [3,undefined]
move({}) = [undefined,undefined]
move() = [0,0]

变量的解构赋值用途

  • 交换变量的值
let x = 1
let y = 2
[x,y] = [y,x]
  • 从函数返回多个值
function example(){
    return[1,2,3]
}
let [a,b,c] = example()

function example(){
    return {foo:1,bar:2}
}
let {foo,bar} = example()
  • 函数参数的定义
function f([x,z,y]){...}
f([1,2,3])

function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
  • 提取JSON数据
let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let {id,status,data:number} = jsonData
number =  [867, 5309]
  • 函数参数的默认值
jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
} = {}) {
  // ... do stuff
};
  • 遍历map结构
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
  console.log(key + " is " + value);
}
// first is hello
// second is world
  • 输入模块的指定方法

加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。

const { SourceMapConsumer, SourceNode } = require("source-map");

草莓酱少女
6 声望1 粉丝