ES6+知识点汇总(3)— 字面量增强、参数默认值、剩余参数、展开运算符

151 阅读10分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情

七、对象字面量增强

对象字面量是一种对象的写法,对象的写法有两种:

  • 一种是通过实例化构造函数来生成对象
const person = new Object();
person.name = 'zs';
person.age = 18;
person.sayHi = function() {
  console.log('hi');
}
  • 一种则是通过字面量的写法,相对于实例构造函数的方式来说更加直观一些
const person = {
  name: 'zs',
  age: 18,
  sayHi : function() {
    console.log('hi');
  }
};

我们实际上很少使用实例化构造函数来声明对象,更对的会使用字面量的写法来声明对象,更加直观。

7-1、属性的简洁表示

当对象的键名和变量或常量名一样的时候,可以只写一个

const age = 18;
const person = {
  age // 相当于"age": age
};

7-2、方法的简洁表示

省略冒号与关键字function,直接通过方法名()的方式编写

const person = {
  //sayHi : function() {
  //	console.log('hi');
  //}
  sayHi() {
    console.log('hi');
  }
};

7-3、方括号语法的增强

7-3-1、方括号语法的用法

我们可以利用方括号语法来给对象添加通过变量定义的属性:

  • 在方括号语法增强前,我们只能先声明对象,再给该对象通过“对象[属性变量]=xx”的方式添加属性值
  • 在方括号语法增强后,我们可以在声明对象的同时通过方括号语法给对象添加属性
const prop = 'age';
// 增强前
// const person = {};
// person[prop] = 18;

// 增强后
const person = {
  [prop]: 18
};

7-3-2、方括号中可以放什么

方括号中除了可以放变量名外,还可以放通过计算可以得到值的表达式

const func = () => 'age';
const person = {
  [func()]: 18
};

当然也可以放字符串、或在其中进行字符串拼接等:

const person = {
  ['age'+'s']: 18
};

7-3-3、方括号语法与点语法的区别

  • 属性名由字母、数字、下划线以及"$"符号构成,且数字不能在开头的时候(符合JS命名规范)可以使用点语法,也可以使用方括号语法,属性名不是合法标识符时,只能使用方括号语法
const person = {
  "age": 18,
  "3e": "123"
}
// age属性名是合法标识符,使用点语法与方括号语法均可
console.log(person.age);
console.log(person['age']);
// 3e是不合法标识符,只能使用方括号语法
// console.log(person.3e); // 会报错
console.log(person['3e']);
  • 使用变量或常量保存属性名时,只能使用方括号语法,不能使用点语法。
const person = {
  "age": 18
}
const prop = "age";
console.log(person[prop]); // 18
console.log(person.prop); // undefined

以上代码中,由于属性名是一个常量,只能使用方括号语法访问,访问时会使用prop中保存的值:"age",等价于person.age

八、函数参数的默认值

8-1、基本用法

在之前,我们如果当参数未传递时使用默认值会这样实现:

比如,我们要相加a与b,如果b未传递则当成1来处理

const add = (a, b) => {
  if (typeof b === 'undefined') {
    b = 1;
  }
  return a + b;
}

可见,如果使用这种方式在存在多个参数的情况下为多个参数设置默认值就会很麻烦了,所以引入了函数参数的默认值。

const add = (a, b = 1) => a + b;

8-2、使用函数参数默认值的注意事项

8-2-1、默认值的生效条件

不传参数或者传递undefined作为参数时,默认值才生效

const add = (a, b = 1) => a + b;
add(2, null); // null不严格等于undefined,所以会将null转换成0再与2相加,结果为2
add(2, undefined);// 3
add(2);// 3

8-2-2、默认值表达式

如果默认值是表达式,则是惰性求值的

function fn() {
  console.log(111);
  return 1;
}
function test(a = fn()) {}
test(); // 会打印出来111
test(undefined); // 会打印出来111
test(2); // 不会打印出111,因为参数赋值为表达式,表达式是惰性求值的,此时传递了内容就没有用到默认值,所以不会执行fn

8-2-3、设置默认值技巧

函数参数的默认值,最好从参数列表的右边进行设置,不然调用起来比较麻烦:

const add = (a = 1, b) => a + b;
add(undefined, 2);
  • 如果从前面开始设置,调用的时候必须要设置为undefined才行

九、剩余参数

9-1、为什么会引入剩余参数

在ES5中,函数经常会传入不定参数,在传入不定参数时,ES5给出的解决方案时通过arguments对象来获取传递给函数的参数。而arguments对象不是一个数组,是一个类数组对象(即:可以通过索引属性访问元素并且拥有length属性的对象),尽管arguments是一个类数组且可遍历的变量,但它不是数组,不支持数组的方法。(比如:不能调用arguments.forEach...方法)

所以要想使用arguments调取数组方法,需要借助call方法把arguments转化成一个真正的过程:

function fn() {
  var arr = [].slice.call(arguments);
  console.log(arr)
}
fn('ES6'); //  ["ES6"]
fn('haha', 123); //  ["haha", 123]

但是这样无疑是一个繁琐的过程,而且不容易理解,所以ES6给出了“剩余参数”的解决方案。

当一个函数需要接收多个参数但不确定具体传递几个参数时,我们会使用剩余参数:

function fun(a, b, ...args) {
  console.log(a, b, args);
}
fun(1,2,3); // 1 2 [3]
fun(1,2); // 1 2 []
fun(1); // 1 undefined []
fun(1,2,3,4,5); // 1 2 [3,4,5]
  • 剩余参数使用"..."紧跟着一个参数去接收

  • 剩余参数的本质是一个数组,当我们传递多个参数时,参数会一一与参数列表对应起来,多出来参数会被放入该数组中,这样我们就摆脱了arguments的束缚

9-2、使用剩余参数注意事项

9-2-1、当在箭头函数中使用剩余参数时

需要注意的是箭头函数中即使只有一个剩余参数没有其他参数时,也不能省略“()”

const fun = (...args) => console.log(args);

9-2-2、可以使用剩余参数替代arguments

我们知道在箭头函数中是没有arguments的,当我们既想使用箭头函数又想使用arguments来获取实际参数,这个时候就可以使用剩余参数替代arguments去获取实际参数:

const fun = (...args) => {
  console.log(args[0]);
}
  • 除此之外,由于剩余参数本身就是个数组,所以可以使用数组的相关方法

9-2-3、剩余参数的位置

剩余参数只能作为最后一个参数,之后不能再有其他参数,否则会报错:

const fun = (a, b, ...args) => {}

十、展开运算符

展开运算符可以对数组使用,也可以对对象使用

10-1、数组使用展开运算符

10-1-1、认识数组的展开运算符

通过一个小例子来认识数组的展开运算符:

比如,我们想计算出一个数组中的最小值,JS为我们提供了Math.min()方法,但是Math.min()接收的参数不是数组而是接收一个参数列表,所以我们可以将数组形式转换为参数列表的形式,那么就可以使用展开运算符来对数组进行展开即可:

Math.min(...[3,6,1]);
  • 可见,在数组外侧加...即可将数组转换为参数列表

10-1-2、区分剩余参数与展开运算符

剩余参数与展开运算符最根本的区别为:

  • 剩余参数是将参数列表形式 => 数组形式
  • 展开运算符是将数组形式 => 参数列表形式
const add = (...args) => {} //  剩余参数
const add = (...args) => {
  // args是数组,那么又加了...则这里就是展开运算符饿
  console.log(...args);
}
// 这里在[1,2,3]前加...,是将[1,2,3]展开,这样就变成了一个一维数组
console.log([...[1,2,3],4,5]);

10-1-3、数组展开运算符的应用

10-1-3-1、复制数组

ES5中经常会遇到数组的浅拷贝,但是数组是引用类型,不能直接像字符串那样直接赋值,在ES5中数组的拷贝往往通过循环来实现:

var arr1 = [1, 2, 3];
var arr2 = [];
arr1.forEach(function(value){
  arr2.push(value);
}) 
console.log(arr2);	// [1, 2, 3]

也可以使用数组的concat和slice方法来实现:

var arr1 = [1, 2, 3];
var arr2 = [].concat(arr1);
var arr3 = arr1.slice(0);
arr1.push(4)
console.log(arr1); //[1, 2, 3, 4]
console.log(arr2); //[1, 2, 3]
console.log(arr3); //[1, 2, 3]

那么在ES6中,我们可以通过展开运算符来实现:

var arr1 = [1, 2, 3]; 
var arr2 = [...arr1];
arr1.push(4)
console.log(arr1);	//[1, 2, 3, 4]
console.log(arr2);	//[1, 2, 3]
10-1-3-2、合并数组

在ES5中,我们合并数组会通过concat来实现,在ES6中我们可以通过展开运算符来实现:

const a = [1,2,3];
const b = [4,5,6];
const c = [7];
console.log([...a, ...b, ...c]); // [1,2,3,4,5,6,7]
console.log([...a, ...c, ...b]);// [1,2,3,7,4,5,6]
console.log([0, ...a, ...b, ...c, 8, 9]) // [0,1,2,3,4,5,6,7,8,9]

使用展开运算符不仅可以很方便的实现合并数组,也可以按照我们的想法来更换数组元素的位置或添加新的内容进去

10-1-3-3、字符串转为数组

ES5中我们可以使用字符串.split('')的方式将字符串转为数组,ES6中我们可以结合展开运算符来将字符串转换为数组:

const str = 'abc';
// ES5
console.log(str.split('')); // [a,b,c]

// ES6
// console.log(...str); // a b c
console.log([...str]); // [a,b,c]
  • 在字符串前使用...来将字符串进行展开,如果将展开的字符串放入数组中,只需在外侧添加[]即可

我们也结合展开运算符来实现字符串的倒序:

const str = '123';
console.log([...str].reverse().join(''));
10-1-3-4、类数组转换为数组

我们可以使用展开运算符将arguments变为数组:

function fun() {
  console.log([...arguments]);
}
fun(1,2);

也将NodeList变为数组:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <p></p>
</body>
<script>
  console.log([...document.querySelectorAll('p')]);
</script>

</html>

将类数组arguments/NodeList变为普通数组后就可以使用数组相关方法了

10-2、对象使用展开运算符

10-2-1、展开对象

const apple = {
  color: '红色',
  shape: '球形',
  taste: '甜'
};

// 对象的展开:把属性罗列出来,用逗号分隔,放到一个 {} 中,构成新对象
console.log({ ...apple });
console.log({ ...apple } === apple);

需要注意,与展开数组不同的是,对象不能直接展开,必须在{}中进行展开,得到一个新的对象。

10-2-2、合并对象

  const apple = {
    color: '红色',
    shape: '球形',
    taste: '甜'
  };
  const pen = {
    color: '黑色',
    shape: '圆柱形',
    use: '写字'
  };
  console.log({ ...apple, ...pen });

  • 合并对象时,当多个对象中有相同属性时,后面对象的属性会覆盖前面的

10-2-3、对象展开运算符的注意事项

10-2-3-1、对空对象的展开

当我们展开一个空对象的时候,时没有任何效果的

console.log({...{}}); // {}
10-2-3-2、非对象的展开

如果展开的不是一个对象,那么会自动将其转换为对象,然后再将其中的属性罗列出来

console.log({...1}); // {}
console.log({...undefined}); // {}
console.log({...null}); // {}
console.log({...true}); // {}

以上代码将1、undefined、null、true转换为对象后,其对象本身上没有任何属性可以罗列,所以都会是空对象

比较特殊的是,如果在{}中展开一个字符串,会自动给转成一个类似数组的对象,因此返回的不是空对象:

console.log({...'abc'});

如果我们在{}中展开一个数组呢?

console.log({ ...[1, 2, 3] });

可见会将数组中的值当成键值对来一一罗列出来。

10-2-3-3、对象中对象属性的展开

当对象中存在对象属性时,不会对该属性进一步展开:

  const apple = {
    feature: {
      taste: '甜'
    }
  };
  const pen = {
    feature: {
      color: '黑色',
      shape: '圆柱形'
    },
    use: '写字'
  };
  console.log({ ...apple });
  console.log({ ...apple, ...pen });
  // 相当于
  console.log({
    feature: {
      color: '黑色',
      shape: '圆柱形'
    },
    use: '写字'
  });

由于feature是对象中的对象属性,不会进一步对其展开,当合并apple与pen时,pen中的同名feature属性会直接覆盖掉apple中的feature属性,所以最终打印结果为:

10-2-4、复制对象

const a = { x: 1, y: 2 };

const b = { ...a };
console.log(a === b); // false