# v-if和v-show的区别:
v-if是操作DOM元素,v-show是不操作DOM元素,控制css中样式的显示和隐藏。
v-if的切换消耗大,v-show的初始化消耗大。
v-for为什么不能和v-if同时使用:
v-for的优先级比v-if的优先级高,如果在同一元素上,渲染时,消耗性能,先循环再判断,
如果要避免的话,先在外层套一个template,进行v-if判断,在内部用v-for
如果条件在循环内的话,可用计算属性compted提前过滤出不逊要显示的项目。
# v-for中的key有什么用:
用来更高效的对比虚拟DOM的节点是否相同。
提高渲染效率,如果一个虚拟DOM元素的节点发生变化,不用重新渲染所有DOM,就渲染改变的
DOM元素。
#watch和compated的区别:
watch是对数据data的监听,compated是对数据的计算,都是根据数据的变化来显示,
一般compated能做到的,watch也可以做到,compated有缓存
watch可以监听异步操作,高消耗,watch有deep,和immedate两种监听方式。
#map和forEach的区别:
map返回一个新数组,支持return,forEach不返回新数组,不支持return。
forEach举例:
var array = [10,34,57,43,76];
var res = array.forEach(function (item,index,input) {
input[index] = item*10;
})
console.log(res);//--> undefined;
console.log(array);//--> 通过数组索引改变了原数组;
[100,340,570,430,760]
map举例:
var array = [10,34,57,43,76];
var res = array.map(function (item,index,input) {
return item*10;
})
console.log(res);
console.log(array);不变
each举例:不支持return
$.each(arr,function(index,value){
xxxx
})
$.each( ["a","b","c"], function(i, v){
alert( i + ": " + v );
});
#module语法:import和export:
#promise对象aynsc和awiti
promise是一个对象,可以用来传递异步任务的消息。我们知道异步任务的执行时间和执行结果是无法预测的,但是在实际项目中我们想要在异步任务执行之前,就根据不同的执行结果做出相应的处理。所以这时候就需要Promise这个用于获取异步任务消息的对象。
使用场景:
处理异步任务。因为JS是单线程的,使用Promise可以将异步任务以同步的方式展现出来,避免了层层的回调函数。
Promise对象特点:
Promise对象三种状态:
Pending(进行中)、resolved(已完成)、Reject(已失败)。
只有异步任务的结果能唯一确定promise对象的状态。
并且,一旦状态改变了之后,就固定了,任何的操作都无法改变当前状态。
promise的构造函数接受一个函数作为参数。函数的两个参数分别是函数resolve和函数reject。
如果异步任务成功就用resolve方法将promise的状态从pending改为resolved,如果异步任务失败,就用reject改变Promise的状态,从pending改为rejected。
newPromise(resolve,reject)=>{resolve();reject();}
// 一个Promise实例constp=newPromise((resolve,reject)=>{// do somethingif(success){resolve(results)}else{reject(err);}}).then((results)=>{console.log(results);}).catch((err)=>{console.log(err);})
上图是一个Promise实例。P是生成的Promise实例,一般字promise构造函数的内部会包裹一个异步函数。当实例生成之后,可以用then方法分别指定异步函数成功或者失败的时候的回调函数。results就是异步任务成功时候的返回的结果,一般then的回调函数包括两个,第一个是resolve时候的回调函数,第二个是reject时候的回调函数,第二个参数可以省略。
Promise的方法:
p.then((results)={},(err)=>{});
注意Promise新建之后会立刻执行,而then方法会在当前脚本所有同步任务执行完之后才会开始执行。
<script>window.onload = ()=>{ // 创建3个异步任务 const pro1 = ()=>{ return new Promise((reject, resolve)=>{ setTimeout(()=>{ console.log('执行异步任务1'); resolve('异步任务1执行成功'); }, 2000); }); }</script>
我来仔细剥离一下这个上面的pro1;
最外层 const pro1 = ()=>{} 可以看出pro1是一个函数。
然后pro1这个函数的返回值是一个Promise对象。
这个promise对象是通过构造函数构造的,他的构造函数参数是一个函数,这个函数是:
(reject,resolve)=>{setTimeout(()=>{console.log('执行异步任务1');resolve('异步任务1执行成功');},2000);}*/// 一般这个函数内部都会包裹一个异步任务 }
promise.all
异步函数(async/await)
用async包裹一个函数,可以把这个函数变成一个异步函数。但是也只是让这个函数具有异步特性,执行上还是同步执行的。
let fun1 = async ()=>{}
async函数的返回值
是一个Promise对象
asyncfunctionfoo(){console.log(1);return3;// 等价于return Promise.resolve(3);}foo().then(console.log);console.log(2);// result: 1 2 3
await
使用await可以暂停异步函数代码的执行。 await只能在async中使用 。
await和async一起使用的时候,起作用的其实只有await,因为只有async的话,他的执行和普通函数没有什么区别
async()=>{// do aawaitP// do b}
以上代码的执行顺序是,进入异步函数之后执行do a 遇到await则暂停异步任务的执行,等待await右侧的p执行完毕之后,恢复异步任务的执行,执行do b。一般情况下这个P是一个Promise
asyncfunctionfoo(){console.log(2);console.log(awaitPromise.resolve(8));console.log(9);}asyncfunctionbar(){console.log(4);console.log(await6);console.log(7);}console.log(1);foo();console.log(3);bar();console.log(5);// result 1 2 3 4 5 6 7 8 9
8个生命周期
beforecreate ,created,beforemount mounted(真实的DOM挂在完成,数据完成素箱数据绑定,可以访问到Dom节点),beforeupdate(数据更新),update(Dom更新),beforedestory,destoryed
vue组件通信
父->子间通信props 子->父通信$emit
兄弟间通信Bus实现跨组件之间通信
vuex是一种状态管理系统
State--this.$store.state.xxx 取值
Getter--this.$store.getter.xxx 取值
Mutation--this.$store.commit("xxx") 赋值
Action--this.$store.dispatch("xxx") 赋值
Module
#set和map的数据结构:
、Set数据结构
1、ES6提供了新的数据类型Set。它类似于数组,但是成员的值都是唯一的,没有重复的值
2、Set的实例
Set本身是一个构造函数,用来生成Set数据结构,Set函数接受一个数组或者具有iterable接口的其他数据结构作为参数,用来初始化
const set = new Set([1,2,3,4,4])
console.log(set);//Set(4) { 1, 2, 3, 4 }
console.log([...set]);//[ 1, 2, 3, 4 ]
const s1 = new Set('hello')
console.log(s1);//Set(4) { 'h', 'e', 'l', 'o' }
console.log([...s1]);//[ 'h', 'e', 'l', 'o' ]
3、Set的用途
// 数组去重
console.log([...new Set([1,2,1,2,2,5,3,2])]);
//[1,2,5,3]
//数组去重的另一种方法
function dedupe(array) {
return Array.from(new Set(array))
}
console.log(dedupe([1,1,2,2,3,3]));//[1,2,3]
// 字符串去重
console.log([...new Set('helloe')].join(''));
//'helo'
4、Set实例的属性
(1) size 返回实例的成员总数
let s = new Set([1,2,3,4,5])
console.log(s.size);//5
(2)constructor 返回构造函数
let s = new Set([1,2,3,4,5])
console.log(s.constructor);
//[Function: Set]相当于Set()
5、Set实例的操作方法
let s = new Set([1,2,3,4,5])
//1、add(value),添加某个值,返回实例本身
console.log(s.add('a'));//Set(6) { 1, 2, 3, 4, 5, 'a' }
//2、delete(value),删除某个值,返回布尔值,表示删除是否成功
console.log(s.delete(2));//true
//3、has(value),检测该值是否是实例的成员,返回布尔值
console.log(s.has(4));//true
//4、clear(),清空所有成员,没有返回值
s.clear()
console.log(s);//Set(0) {}
6、 Set实例的遍历方法
由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
let s = new Set([1,2,3,4,5])
//1、keys() 返回键名的遍历器
console.log(s.keys());//[Set Iterator] { 1, 2, 3, 4, 5 }
for(let i of s.keys()){
console.log(i);//1 2 3 4 5
}
//2、values() 返回键值的遍历器
for(let i of s.values()){
console.log(i);//1 2 3 4 5
}
//3、entries() 返回键值对的遍历器
for (const i of s.entries()) {
console.log(i);//[1,1] [2,2] [3,3] [4,4] [5,5]
}
//4、forEach() 遍历Set实例,和数组用法一样
s.forEach((val,index,s)=>console.log(val+'---'+index+'---'+s))
1---1---[object Set]
2---2---[object Set]
3---3---[object Set]
4---4---[object Set]
5---5---[object Set]
二、Map数据结构
ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值都可以当作键。Object结构提供了“字符串—值的对应”,Map结构提供了“值—值的对应”
1、创建Map的实例
Map构造函数接受一个数组作为参数,这个数组的元素是一个一个表示键值对的数组
const map = new Map([['name','lisi'],['age',23]])
console.log(map);
//Map(2) { 'name' => 'lisi', 'age' => 23 }
2、Map结构实例的属性
const map = new Map([['name','lisi'],['age',23]])
//1、size 返回实例的成员总数
console.log(map.size);//2
//2、constructor 返回Map构造函数
console.log(map.constructor);//[Function: Map]相当于Map(、 Map结构实例的操作方法
const map = new Map([['name','lisi'],['age',23]])
//1、set(key,value) 给实例添加键值对,返回实例本身,如果键已经存在,对应的键值将被修改
console.log(map.set('height',70));
//Map(3) { 'name' => 'lisi', 'age' => 23, 'height' => 70 }
//2、get(key) 获取key对应的键值,如果找不到key,返回undedined
console.log(map.get('age'));//23
//3、has(key),检测某个键是否存在,返回布尔值
console.log(map.has('age'));//true
//4、delete(key) 删除某个键,返回布尔值
console.log(map.delete('age'));//true
//5、clear() 清除所有成员,没有返回值
map.clear()
console.log(map);
Map(0) {}
4、Map实例的遍历方法
const map = new Map([['name','lisi'],['age',23]])
// 1、keys() 返回键名的遍历器
for(let i of map.keys()){
console.log(i);
// name age
}
// 2、values() 返回键值的遍历器
for(let i of map.values()){
console.log(i);
// lisi 23
}
// 3、entries() 返回键值对的遍历器
for(let i of map.entries()){
console.log(i);
// [ 'name', 'lisi' ] [ 'age', 23 ]
}
// 4、forEach() 遍历所有成员
map.forEach((value,index,map)=>console.log(value + '---' + index + '---' + map))
// lisi---name---[object Map] 23---age---[object Map]
js中的遍历:
for 循环
普通的for循环,也是最常用到的
for(let i = 0;i<arr.length;i++){
console.log(arr[i]);
}
for in
for in 循环 用于遍历对象的属性
如果用其来遍历数组 key值则为下标
let obj = {
age: 18, name: "Joker", sayHello() {
console.log("Hello")
}
};
for (let key in obj) {
console.log(`key:${key},value:${obj[key]}`);
}
for of
for of 语句循环遍历可迭代对象的值。such as:数组,字符串,节点列表…
for (let item of arr) {
console.log(item)
}
forEach
ES5数组自带的循环,主要是用来遍历数组,性能比for循环还弱
arr.forEach((item,index,arr)=>{
console.log(`key:${index},value:${item}`)
})
map
用法与forEach类同,但参数中的函数支持返回值
返回值是一个新的数组,其每项就是参数中的函数返回的值
arr.map((value, index, array) => {
return value === 123;
});
filter
用法与forEach类同,但参数中的函数支持返回值
返回值为一个新的数组,如果参数中的函数返回为真 则会将这一项push进新的数组当中,为假或没有返回,则不会
arr.filter((value, key) => {
return value === 123;//[]
return value.indexOf('o') !== -1;//['Joker', 'north']
})
includes
检测数组当中是不是存在某个元素
console.log(arr.includes('Joker'));//true
console.log(arr.includes('111'));//false
console.log(arr.includes('north'));//true
find
寻找第一个符合条件的元素
console.log(arr.find((value, key, arr) => {
return value.indexOf('o') !== -1;
}));//Joker
findIndex
寻找第一个符合条件元素的索引
console.log(arr.findIndex((value, key, arr) => {
return value.indexOf('o') !== -1;
}));//0
some
数组中是不是有值符合某个条件
所有值中有一个符合条件就不继续执行,返回true
console.log(arr.some((value, key, arr) => {
return value.indexOf('o') !== -1;
}));//true
every
数组中是不是所有值符合某个条件
所有值中有一个不符合条件就不继续执行,返回false
console.log(arr.every((value, key, arr) => {
return value.indexOf('o') !== -1;
}));//false
都是块级作用域,不支持变量提升,const一般是常量定义。
变量的解构赋值:
以前,为变量赋值,只能直接指定值。
let a = 1;
let b = 2;
let c = 3;
es6允许写法
let [ a, b, c ] = [ 1, 2, 3 ];
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。
let [foo, [[bar], baz]] = [1,[[2], 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 // []
如果解构不成功,变量的值就等于undefined。
let [ foo ] = []
let [bar, foo] = [1];
另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
如果等号的右边不是数组(或者严格地说,不是可遍历的结构,参见《Iterator》一章),那么将会报错。
// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
上面的语句都会报错,因为等号右边的值,要么转为对象以后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)。
对于 Set 结构,也可以使用数组的解构赋值。
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"
默认值
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
上面代码中,如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined。
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
function f() {
console.log('aaa');
}
let [x = f()] = [1];
x = 1; 而 函数f 没有去执行
上面的代码中,因为X能取到值,所以函数f根本不会执行,上面的代码其实等价于下面的代码
let x;
if ([1][0] === undefined) {
x = f();
} else {
x = [1][0];
}
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
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
上面最后一个表达式之所以会报错,是因为x用y做默认值时,y还没有声明。
对象的建构赋值
解构不仅可以用于数组,还可以用于对象。
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
如果解构失败,变量的值等于undefined。
对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。
let {log, sin, cos, PI } = Math;
const { log } = console;
log('hello')
上面代码的例一将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。例二将console.log赋值到log变量。
如果变量名与属性名不一致,必须写成下面这样。
let { foo: a } = { foo: 'aaa' }
a // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
这实际上说明,对象的解构赋值是下面形式的简写(参见《对象的扩展》一章)。
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
也就是说,对象的结构赋值的内部机制,是先找同名属性,然后再赋值给对应的变量,真正赋值的是后者,而不是前者
let {foo : baz } = { foo: 'aaa', bar: 'bbb'}
console.log(baz) // aaa
console.log(foo) error: foo is not defined
上面的代码中 foo 是匹配的模式, baz 才是变量, 真正被赋值的是baz,而不是模式 foo
与数组一样,解构也可以用于嵌套解构的对象
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
注意 这里的p是模式,不是变量,因此不会被赋值,如果p也作为变量赋值,可以写成下面这样
let obj = {
p: [
'hello',
{ y: 'world' }
]
}
let { p, p: [ x, { y } ] } = obj;
x // hello
y //world
p // [ 'hello' , {y: 'wrold' } ]
下面一个例子
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}
上面代码有三次解构赋值,分别是对loc、start、line三个属性的解构赋值。注意,最后一次对line属性的解构赋值之中,只有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属性,对应一个子对象。该子对象的bar属性,解构时会报错。原因很简单,因为foo这时等于undefined,再取子属性就会报错。
注意,对象的解构赋值可以取到继承的属性
const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);
const { foo } = obj1;
foo // "bar"
默认值
var {x = 3} = {};
x // 3
var {x, y = 5} = {x: 1};
x // 1
y // 5
var {x: y = 3} = {};
y // 3
var {x: y = 3} = {x: 5};
y // 5
var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
默认值生效的条件是,对象的属性值严格等于undefined。
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
上面代码中,属性x等于null,因为null与undefined不严格相等,所以是个有效的赋值,导致默认值3不会生效。
注意点
(1)如果要将一个已经声明的变量用于解构赋值,必须非常小心。
// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
上面的代码写法错误 因为javascript 引擎会将 {x} 理解成一个代码块,从而发生语法错误,只有不将大括号写在行首,避免js将其解释为代码块,才能解决这个问题
let x;
({x} = { x: 11})
上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行。关于圆括号与解构赋值的关系,参见下文。
由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
上面代码对数组进行对象解构。数组arr的0键对应的值是1,[arr.length - 1]就是2键,对应的值是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
函数参数结构
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]
上面代码中,函数move的参数是一个对象,通过对这个对象进行解构,得到变量x和y的值。如果解构失败,x和y等于默认值
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 ]
用途
交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
上面代码交换变量x和y的值,这样的写法不仅简洁,而且易读,语义非常清晰。
从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。
// 返回一个数组
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, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
提取 JSON 数据
解构赋值对提取 JSON 对象中的数据,尤其有用。
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
函数参数的默认值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
} = {}) {
// ... do stuff
};
遍历 Map 结构
任何部署了 Iterator 接口的对象,都可以用for...of循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。
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
如果只想获取键名,或者只想获取键值,可以写成下面这样。
// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}
输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。
const { SourceMapConsumer, SourceNode } = require("source-map");
例子
function move({x = 0, y = 1} = {}) { // 默认赋值 x: 0, y: 1
console.log([x, y]) //
}
move({ x:111 }); // [111, 1]
move(); // [0, 1]
move({}); // [0, 1]
function f({x, y} = {x: 0, y: 0}) { //
console.log([x, y])
}
f(); // [0,0]
f({}); // [undefined, undefined]