解构赋值语法是一个JavaScript表达式,这使得可以将值从数组或属性从对象提取到不同的变量中。
一.数组的解构赋值
1.普通的数组解构赋值
const arr = [1,2,3,4];
let [a,b,c,d] = arr;//a=1,b=2,c=3,d=4
2.稍复杂的匹配规则
const arr=['a','b',['c','d',['e','f','g']]];
const[ ,b]=arr;//b='b'
const[ , , g]=['e','f','g'];//g='g'
const[ , , g]=['c','d',['e','f','g']];//g=['e','f','g']
const[ , , [ , , g]]=['c','d',['e','f','g']];//g='g'
3.扩展运算符
... 可进行合并数组 [...arr1,...arr2,...arr3] 等价于arr1.concat(arr2,arr3);
const arr1=[1,2,3];
const arr2=['a','b'];
const arr3=['zz',1];
const arr4=[arr1,arr2,arr3];//[[1,2,3],['a','b'],['zz',1]]
const arr5=[...arr1,...arr2,...arr3];//[1,2,3,'a','b','zz',1]
4.拓展运算符和解构数组结合起来使用
const arr=[1,2,3,4,5,6];
const [a,b,...c]=arr;//a=1,b=2,c=[3,4,5,6]
const [a,b,...c,d]=arr;//报错
5.默认值
只有在匹配成undefined时才能设置默认值
const arr = [1,undefined,undefined];
const [a,b,c,d]=arr;//a=1,b=undefined,c=undefined,d=undefined
const [a,b=2,c,d='aaa']=arr;//a=1,b=2,c=undefined,d='aaa'
const arr1=[1,null,undefined];
const [a,b=2,c,d='aaa']=arr1;//b=null,只有是undefined时才会选择默认值
6.交换变量
let a=20;
let b=10;
[a,b]=[b,a];//a=10,b=20
7.接收多个 函数返回值
function getUserInfo(id){
//...ajax
return [
true,
{
name:'小明',
gender:'女',
id:id
},
'请求成功'
];
};
const [status,data,msg]=getUserInfo(12);//status=true,data= {name:'小明',gender:'女',id:123},msg='请求成功'
二.对象的解构赋值
对象的解构赋值:①的解构赋值与数组的机构复制相似。
②等号左右两边都为对象结构 const { a, b } = { a: 1, b: 2 }。
③左边的{}中为需要赋值的变量。
④右边为需要解构的对象。
主要用途:①提取对象属性。
②使用对象传入乱序的函数参数。
③获取多个函数返回值。
与数组不同的是,数组是靠下标匹配,对象是靠属性名匹配,属性名若是不匹配则返回undefined。
当出现了重复的对象属性名时,可以在属性名后添加:改新名字。冒号前面原本所代表的东西是不存在的,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量,真正被赋值的是后者,而不是前者。
对象的解构赋值中,与顺序无关,需要注意的是,变量名与属性名必须一致。
1.普通的对象解构赋值
const obj={
saber:'阿尔托利亚',
archer:'卫宫'
};
const {saber,archer1}=obj;//saber:'阿尔托利亚',archer:undefined
2.稍微复杂点的对象解构赋值
const player={
nickname:'几位佛尔阿文',
master:'东海龙王',
skill:[{
skillName:'龙吟',
mp:'100',
time:6000
},{
skillName:'龙卷雨季',
mp:'400',
time:3000
},{
skillName:'龙腾',
mp:'900',
time:6000
}]
};
const {nickname}=player;// nickname:'几位佛尔阿文'
const {master}=player;// nickname:'东海龙王'
// const {skill}=player;
// const [skill1]=skill;
//类似于
const {skill:[skill1]} = player;
const {skill:[skill1,{skillName},{skillName:sklName}]} = player;//skillName='龙卷雨季',sklName='龙腾'
3.结合拓展运算符
扩展运算符...会将剩下的数据放在一个自己取名的对象中。还可用于对象的合并。
const obj={
saber:'阿尔托利亚',
archer:'卫宫',
lancer:'哈佛导师'
};
const{saber,...oth}=obj;//saber='阿尔托利亚',oth={archer:'卫宫',lancer:'哈佛导师'}
用拓展运算符合并对象
const obj1={
archer:'卫宫',
lancer:'哈佛导师'
}
const obj={
saber:'阿尔托利亚',
...obj1
}//obj={saber:'阿尔托利亚',archer:'卫宫',lancer:'哈佛导师'}
4.如何对已经申明了的变量进行对象的解构赋值
let是用在以后你还有可能再次给他赋值的时候,const 以后不能再给它赋值。
一般 const 定义方法,let 定义变量。
let age;
const obj={
name:'小明',
age:22
};
//{age}=obj;//报错
({age}=obj);//不推荐
更推荐:
const obj={
name:'小明',
age:22
};
let {age}=obj;//age=22
5.默认值
let girlfriend={
name:'小红',
age:22,
food:undefined
};
let {name,age=24,food='苹果',hobby=['学习']}=girlfriend;//name='小红',age=22,food='苹果',,hobby=['学习']
例题
var {foo:baz}={foo:'aaa',bar:'bbb'};
console.log(baz)得到aaa
解析:对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。
在右面对象的内部找到与foo同名的属性 , 然后再把属性foo的值赋给变量foo(左边的为变量foo) . 然后{foo:baz} 结构赋值 , 把前面的变量foo的值赋值给后面的变量baz . 真正被赋值的是后面的变量baz , foo起到了一个中间传递的作用 , 变量foo实际上并没有赋值 ,所以不会输出"aaa" .
6.提取对象属性
const {name, hobby:[hobby1],hobby}={
name:'小红',
hobby:['学习']
};// name='小红',hobby1='学习',hobby:['学习'];
7.使用对象传入乱序的函数参数
function AJAX({
url,
data,
type='get'
}){
console.log(type);//get
}
AJAX({
data:{
a:1
},
url:'/getInfo',
});
8.获取多个 函数返回值
function getUserInfo(uid){
//...ajax
return {
status:true,
data:{
name:'小红'
},
msg:'请求成功'
};
};
const {status,data,msg:message} = getUserInfo(123);
//status=true, data:{name:'小红'},message='请求成功'
三.字符串的解构赋值
1.字符串的解构赋值
const str='I am the bone';
const [a,b,c,...oth] =str;//a='I',b=' ',c='a',oth=["m", " ", "t", "h", "e", " ", "b", "o", "n", "e"];
分解字符串
const [...spStr1]=str;
const spStr2=str.split();
const spStr3=[...str];
//结果都是:["I", " ", "a", "m", " ", "t", "h", "e", " ", "b", "o", "n", "e"]
2.提取属性
length属性和split()方法
const {length,split}=str;//length=13
四.数值与布尔值的解构赋值(了解)
const {valueOf} = 1;
const { toString } = true
每一个类型都会有自己的一些属性和方法 ,字符串有字符串的方法 ,布尔类型有布尔类型的方法....
而js实际上就是把每一个类型的属性和方法包装成对象 。而每一个包装对象都会有valueof 和 toString方法 ,所以不是布尔值是toString的方法 ,而是toString是布尔类型的方法。
当然 ,上面也说了 ,并不单单布尔类型有,不管是字符串类型还是布尔类型又或者其他类型 ,都有 toString方法。
并不说str就是一个对象 ,他是一个字符串 ,只不过当你使用它的属性和方法的时候,例如str.length ,使用length 的原理就是js把str先转换为包装对象 ,然后调用对象中的length 属性。这些js自动执行的 ,他是为了获取到包装对象的方法 ,我们自己并没有手动的把它转换为对象 ,他还是一个字符串。
因为每一个类型在使用它们本身的属性或者方法的时候 ,都是这样的原理 ,所以这里只是拿字符串来举一个例子 。
五.函数参数的解构赋值
function swap([x,y]){
return [y,x];
}
let arr=[1,2];
arr=swap(arr);//[2,1]
1、在命名规则上,构造函数一般是首字母大写,普通函数则是遵照小驼峰式命名法(小驼峰:多个单词拼接,单词首字母大小,首个单词首字母小写)。
2、调用方式不同,构造函数:构造函数内部会创建一个实例(即使用new来实例对象);
普通函数:普通函数的调用不需要使用new来实例。
3、this的指向不同;构造函数内部的this指向是新创建的person实例,而普通函数内部的this指向调用函数的对象(如果没有对象调用,默认为window)
function Computer({
cpu,
memory,
software=['ie6'],
OS='windows 3.5'
}){
console.log(cpu);
console.log(memory);
console.log(software);
console.log(OS);
};
new Computer({
memory:'128G',//不用按顺序
cpu:'80286',
OS:'windows 10'
})
六.实例总结
封装的思想
减少对全局作用域的污染
传入window和document:优化性能,函数中的变量不用去外层搜索window和document
window.person=person:可以全局访问函数person
(function (window,document){
const FINGERS = 10;
const HEAD = 1;
function Person(){
//....
}
window.Person2 = Person;//释放到外面
})(window,document);
定义到原型上,主要是在多次实例化的时候,会在内存中存一份,每次访问的就是同一个(原型中的行为可以被所有实例共享)。
直接写在方法中的,实例化的时候会在每个实例中再复制一份,所以消耗的内存更高。
window.$Msg = Msg表示给window声明一个为‘$msg'的变量,然后把Msg这个对象赋值给前面那个变量
(function (window,document){
let Msg = function (options) {
this._init(options);
}
Msg.prototype._init = function ({ }){};
window.$Msg=Msg;
})(window,document);
注意:1.继承方法分的比较细 , 而且能够集中管理 . 能够节约代码 , 例如定义一个函数A和B , B继承A的话 , 那么父类A的属性与方法继承给B , 代码得到复用 , 减少了B的代码 . 缺点就是如果A的属性和方法修改了 , 会影响子类的属性和方法 .
2.而普通的函数 , 每个功能封装成一个函数 , 如果方法多的话 , 无法继续起来管理 , 但是优点恰恰也是因为 , 每个功能都是独立的 , 不会互相影响.
3.这两种用法在实际开发中都比较常见 , 每个方法都有自身的优点和缺点 ,需要结合实际情况 . 如普通函数优点适用于简单的组件,封装几个独立的函数 , 直接调用比较简便 . 一些大型项目 ,封装一些公共的方法 , 然后继承 , 会比较节约代码 , 利于管理。