ES6到ES10— 解构赋值

192 阅读7分钟

前言

该章为个人ES6解构赋值知识点笔记,内容上参考了阮大的ES6入门,主要讲述了ES6新增的解构赋值功能,记录了解构在不同数据类型中的使用和其一般的使用场景

数组的解构赋值

基本用法

//ES5中的代码
const arr = [1,2,3];
const a = arr[0];
const b = arr[1];
const c = arr[2];

//ES6解构赋值
let  [A,B,C] = [1,2,3];
console.log(A);//1
console.log(B);//2
console.log(C);//3

上述代码,可以从数组中直接按照对应位置进行提取值,然后对变量进行赋值
本质上,这种写法属于"模式匹配",等号两边模式相同,左边变量就会被赋予右边对应的值

数组解构实例

//完全解构
let [a,[[b],[c]]] = [1,[[2],3]];
a//1
b//2
c//3

//存在空值解构
let [ , ,c] = [1,2,3];
c//3
let [x, ,y] = [1,2,3];
x//1
y//3

//拓展运算解构
let [i,...j] = [1,2,3,4];
i//1
j//[2,3,4]

//不完全解构
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 [x,y,...z] = ["a"];
x//"a"
y//undefined 如果一但解构不成功那么就会直接赋值undefined
z//[]

哪些数据不能被解构

等号右边不是可遍历的解构,解构时就会报错,通常来讲就是等号右边的值转换为对象后不具备Iterator接口或者本身就不具备Iterator接口

//转化为对象不具备Iterator
let [a] = 1; //number类型
let [a] = false; //boolean类型
let [a] = NaN; //非数
let [a] = undefined; //undefined类型
let [a] = null; //类型
//本身不具备Iterator接口
let [a] = {}

默认值

解构赋值允许使用默认值,但是需要有几点注意的地方,接下来举例说明

  1. ES6内部使用严格相等运算===,只有在对应解构的值严格等于undefined时才会使用默认值
    let [a==true] = [];
    a//true 
    
    let [a,b=2] = [1];
    a//1
    b//2
    
    let [a,b=2] = [1,undefined];
    a//1
    b//2
    
    let [a,b=2] = [1,null];
    a//1
    b//null b对应解构的值必须严格等于 undefined
  1. 如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有用到时才会求值
function fn(){
	console.log("此时我被使用了");
}
let [x=fn()] = [1];

上述函数fn不会被执行,因为x取到值了,实际上上述代码可以理解为

let x = [1][0] === undefined ? f() : [1][0];
  1. 默认值可以引用解构赋值的其他变量,但是该变量必须已经声明
let [x=1,y=x] = []
x//1
y//1
let [x=y,y=1] = []
//报错此时y还没有被声明

对象的解构赋值

对象的解构赋值和数组不同的地方是,数字组是按次序排列结构的,但是对象是必须和属性同名才可以取到正确的值,以下举几处实例,展示对象解构的注意点

  1. 按属性名称不按顺序解构
let {bar,foo} = {foo:"a",bar:"b"};
bar//b
foo//a
  1. 当没有属性名对应时解构失败 为undefined
let {foo} = {bar:"baz"};
foo//undefined
  1. 对象解构复制可以很方便将现有的对象方法,赋值到某个变量
const {log} = console;
log("hello")//hello
  1. 解构赋值的简写和重命名
//之前的对象解构方法是一种简写方式,实际上的解构赋值完整写法
let {foo:foo} = {foo:1};
foo//1
//重命名
let {foo:a} = {foo:1};
a//1

以上要注意 foo 是被寻找的同名属性 a 才是真正被赋值的,也就是说对象解构时 : 左边的是匹配模式右边才是赋值变量
5. 对象解构嵌套

let obj = {};
let arr = [];
({foo:obj.prop,bar:arr[0]} = {foo:123,bar:true});
obj//{prop:123}
arr//[true]
  1. 解构报错
let {foo:{bar}} = {baz:"baz"};
//报错 foo解构到时undefined undefined.bar 就会报错
  1. 可解构继承属性
const obj1 = {};
const obj2 = {foo:"bar"};
Object.setPrototypeOf(obj1,obj2);
const {foo} = obj1;
foo//"bar"

默认值

对象解构的默认值规则和数组一样,但是有几点需要注意,以下举例说明

  1. 如果将已经声明的变量用于解构赋值,需要用括号包裹
let x;
{x} = {x:1}
//直接书写时 JS引擎会将{x}解释为代码块,从而发生语法错误
//需要改为以下表达式的写法
({x} = {x:1})//改为表达式就不会报错
  1. 解构赋值允许等号左边的模式中不放任何变量名,因此可以写出以下无意义但是不报错的表达式
({} = {age:1})
  1. JS中数据也是特殊对象,因此可以对数组进行对象属性的解构
let {0:frist,[arr.length - 1] : last} = [1,2,3] ;
first//1
last//3

字符串的解构赋值

字符串解构相对简单,有两种不同的方式,举例说明

  1. 字符串被转换为类似数组的对象,按对应位置进行赋值
const [a,b,c] = "hello";
a//"h"
b//"e"
c//"l"
  1. length属性解构
let {length:len} = "hello"
len//5

数值和布尔值的解构赋值

如果是 数值和布尔值的解构,会先转为对象

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

值得注意的是,只要等号右边的值不是对象或者数组,将其先转为对象然后解构,但是有两个特殊情况,undefined和null无法转化为对象,因此解构时会报错

函数参数的解构赋值

函数的参数也可以进行解构

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

上述代码中,函数的实参是一个数组,但是形参会解构实参的数组,对于函数内部来说参数就是x,y,但是需要注意一但使用函数参数解构,就要注意参数传入,一但类型不对,极容易报错,需要谨慎使用

function add([x,y]){
	return x+y ;
}
add(1)//报错 TypeError: undefined is not a function

参数解构的默认值

function move({x = 0,y = 0} = {}){
	return [x,y];
}
move({x:3,y:8});//[3,8]
move({x:3});//[3,0]

圆括号问题

emmmm,出现可能性较小,直接举例简单说明,一下情况注意不能使用圆括号

  1. 变量声明语句
let [(a)] = [1];
let {x:(c)} = {};
  1. 函数参数
function fn([(z)]){
	return z
}
  1. 赋值语句的模式
({p:a}) = {p:1};

可以使用圆括号的情况:
只有一种: 赋值语句非模式部分

[(b)] = [3]; //正确

用法

解构赋值的用途很多,使用得当将极大的方便前端开发,以下举例使用解构赋值的几种场景

变量交换

let a = 1;
let b = 2;
[b,a] = [a,b];

从函数内解构返回多个值

函数只能返回一个值,如果要返回多个值,那么只能返回一个对象或者数组,如果使用解构就非常方便

function fn (){
    return {
    	foo:1,
        bar:2
    }    
}
let {foo,bar} = fn()

函数参数的定义

我们知道JS函数参数在传入的时候,不会按照名称传入,而是按照顺序传入,使用解构就能很好的实现对应参数的传入

function fn({x,y,z}){...}
fn({z:3,x:1,y:2});

JSON提取数据

let jsonData = {
	id:1,
    status:"OK",
    data:[11,22]
};
let {id,status,data:numbers} = jsonData;
id//1
numbers//[11,22]

函数参数默认值

$.ajax = function(url,{
    async:true,
    beforeSend:function(){},
    //...more config
}={}){
    //...
}

通过解构和默认值的方式,避免了在函数体内部如果配置参数不全再写兼容语句,如 var foo = config.foo||"default foo"

遍历map结构

任何部署了Iterator接口的对象,都可以用for...of循环遍历,因此for...of配合解构,对map进行遍历能很好的获取键值

const map  = new Map();
map.set("frist","hello");
map.set("second","world");
for(let[key,value] of map){
    //内部获取键值
    console.log(`${key} is ${value}`)
}

模块引入

当我们在引入模块时,想要指定引入某些特定方法时,使用解构会非常清晰

//假设我们有一个模块fns,我们想引入其中的fn1方法和fn2方法
const {fn1 , fn2} = require("fns");

结语

不积跬步,无至千里,相信每日细小的积累,终会在日后有所爆发,学无止境与君共勉.