js 解构赋值

Javascript ES6 有 许多特性其实是为了简化代码。解构运算符扩展运算符,和rest运算符就是其中很好的特性,它们可以通过减少赋值语句的使用,或者减少通过下标访问数组或对象的方式,使代码更加简洁优雅,可读性更佳。

*1.解构作用

**
可以快速取得数组或对象当中的元素或属性,而无需使用arr[x]或者obj[key]等传统方式进行赋值

1.1 数组解构赋值:
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

let arr = [1, 2, 3];

//传统方式
let a = arr[0],
    b = arr[1],
    c = arr[2];

//解构赋值,是不是简洁很多?
let [a, b, c] = arr

console.log(a);//1
console.log(b);//2
console.log(c);//3

本质上,这种写法属于模式匹配,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

let [foo, [[bar], baz]] = [1, [[2], 3]]
foo // 1
bar // 2
baz // 3

//不取的,可以用逗号跳过
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

//不取的,可以用逗号跳过
let [x, , y] = [1, 2, 3]
x // 1
y // 3

let [head, ...tail] = [1, 2, 3, 4]
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a']
x // "a"
y // undefined
z // []

不完全解构:即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。

let [x, y] = [1, 2, 3]
x // 1
y // 2

let [a, [b], d] = [1, [2, 3], 4]
a // 1
b // 2
d // 4

默认值
解构赋值允许指定默认值。

let [foo = true] = []
// foo=true
let [a=1,b=2] = []
//a=1
//b=2

let [x, y = 'b'] = ['a'] // x='a', y='b'

//注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。
//所以,只有当一个数组成员严格等于undefined,默认值才会生效。
let [x, y = 'b'] = ['a', undefined] 
// x='a'
// y='b'

//数组成员是null,默认值就不会生效,因为null不严格等于undefined。
let [x, y = 'b'] = ['a', null] 
// x='a'
// y=null

let [x,y] = [] 
// x:undefined
// y:undefined

变量互换

var x = 1,
    y = 2;
var [x, y] = [y, x];

console.log(x); //2
console.log(y); //1

默认值表达式

. // 3.默认值表达式.
//.如果默认值是表达式,默认值表达式是惰性求值的
const func = (name) => {
	console. log( ${name}被执行了");
	return 2;
};
const [x1 = func('x1')] = [1];
const [x2 = func('x2')] = [];

console.log(x1);
console.log(x2);
x2被执行了

元素节点

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8" />
    <title>常见的类数组的解构赋值</title>
</head>
 
<body>
    <p>123</p>
    <p>321</p>
    <p>34567</p>
    <script>
        // NodeList
        console.log(document.querySelectorAll('p'));
        const [p1, p2, p3] = document.querySelectorAll('p');
        console.log(p1, p2, p3);
        //p1: <p>123</p>
        //p2: <p>321</p>
        //p3: <p>34567</p>
    </script>
</body>

函数参数的解构赋值

const array = [1, 1];
const add1 = arr => arr[0] + arr[1];
console.log(add1(array));
const add2 = ([x, y]) =>x+ y;
console.log(add2(array));
2
2

1.2 对象解构赋值

解构不仅可以用于数组,还可以用于对象。

//缩写版
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }
foo // "aaa"
bar // "bbb"

//完整版
let { foo:foo, bar:bar } = { foo: 'aaa', bar: 'bbb' }
foo // "aaa"
bar // "bbb"

//取别名版
let { foo:f, bar:b } = { foo: 'aaa', bar: 'bbb' }
f// "aaa"
b// "bbb"


let obj = {
    name: '小红',
    sex: '女',
    age: 26,
    son: {
      sonname: 'panda',
      sonsex: '男',
      sonage: 2
    }
  }

  const {name, sex, age, son} = obj
  console.log(name + ' ' + sex + ' ' + age) //小红 女 26
  console.log(son)  // { sonname: 'panda', sonsex: '男', sonage: 2 }

};

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

// 等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响
let { bar, foo } = { foo: 'aaa', bar: 'bbb' }
foo // "aaa"
bar // "bbb"

let { baz } = { foo: 'aaa', bar: 'bbb' }
baz // undefined,变量没有对应的同名属性,导致取不到值

对象解构赋值的默认值

const { userName = '小红',age=18 } = {userName:'小明'}
//userName:小明
//age:18

将一个已经声明的变量用于解构赋值,需要括号包着,成一个整体代码块

//一个已经声明的变量用于解构赋值,需要括号包着
let x= 2
( {x} = {x:1} )
//x:1

{x} = {x:1}
//undefined

解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式。
表达式虽然毫无意义,但是语法是合法的,可以执行
在这里插入图片描述

对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量

// 例一
let { log, sin, cos } = Math;

// 例二
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'

函数参数的解构赋值

const  func1 = (user)=>{
	console.log(user.name,user.age)
}
//等同于
const  func2 = ({name,age})=>{
	console.log(name,age)
}
func1({name:'小红',age:18})
func2({name:'小红',age:18})

由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

//数组arr的0键对应的值是1,[arr.length - 1]就是2键,对应的值是3。
//方括号这种写法,属于“属性名表达式”。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3

字符串的解构赋值

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {length : len} = 'hello';
len // 5

1.4数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

1.5 函数参数的解构赋值
函数的参数也可以使用解构赋值。

function add([x, y]){
  return x + y;
}

add([1, 2]); // 3

上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量x和y。对于函数内部的代码来说,它们能感受到的参数就是x和y。

下面是另一个例子。

[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]

函数参数的解构也可以使用默认值。

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]

注意,下面的写法会得到不一样的结果。

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]

上面代码是为函数move的参数指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种写法不同的结果。

undefined就会触发函数参数的默认值。

[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]

1.6 for循环解构:

var arr = [[11, 12], [21, 22], [31, 32]];
for (let [a, b] of arr) {
    console.log(a);
    console.log(b);
}
//11
//12
//21
//22
//31
//32

2.扩展运算符
扩展运算符(spread运算符)用三个点号(…)表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值。

let foo = function(a, b, c) {
    console.log(a);
    console.log(b);
    console.log(c);
}

let arr = [1, 2, 3];
//传统写法
foo(arr[0], arr[1], arr[2]);

//使用扩展运算符
foo(...arr);
//1
//2
//3

特殊应用场景:

数组深拷贝-数组中的元素为基本类型,若为object,依然会拷贝引用地址

var arr = [1, 2, 3];
var arr2 = arr;
var arr3 = [...arr];
console.log(arr===arr2); //true, 说明arr和arr2指向同一个数组的引用地址
console.log(arr===arr3); //false, 说明arr3和arr指向不同数组引用,在堆内存中为arr3另外分配了内存空间

把一个数组插入另一个数组字面量

var arr4 = [...arr, 4, 5, 6];
console.log(arr4);//[1, 2, 3, 4, 5, 6]

字符串转数组

var str = 'love';
var arr5 = [...str];
console.log(arr5);//[ 'l', 'o', 'v', 'e' ]

函数调用

 function push(array, ...items) {
    array.push(...items)
  }

  function add(x, y) {
    return x + y
  }

  const numbers = [4, 38]
  add(...numbers) // 42

替代数组的 apply 方法
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。

// ES5 的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6 的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f(...args);

另一个例子是通过push函数,将一个数组添加到另一个数组的尾部。

// ES5 的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);

// ES6 的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2)

3.rest运算符

rest运算符也是三个点号,不过其功能与扩展运算符恰好相反,把逗号隔开的值序列组合成一个数组。

//主要用于不定参数,所以ES6开始可以不再使用arguments对象
var bar = function(...args) {
    for (let el of args) {
        console.log(el);
    }
}

bar(1, 2, 3, 4);
//1
//2
//3
//4

bar = function(a, ...args) {
    console.log(a);
    console.log(args);
}

bar(1, 2, 3, 4);
//1
//[ 2, 3, 4 ]
rest运算符配合解构使用:
// 数组
const [a, ...rest] = [1, 2, 3, 4];
console.log(a);//1
console.log(rest);//[2, 3, 4]

// 对象配合
const {age, ...rest} = {name: 'qxj', age: 24, hobby: 'write code'};
console.log(age);					//24
console.log(rest);					//{ name: 'qxj', hobby: 'write code' }

rest运算符和spread运算符的区别:

spread运算符:放在赋值一方,即放在实参或者等号右边
rest运算符:放在被赋值一方,即放在形参或者等号左边

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值