JavaScript基础之ES6语法(下) | 青训营

95 阅读7分钟

剩余参数和扩展运算符

剩余参数

剩余参数允许我们将不确定数量的剩余的元素放到一个数组中。

比如说,当函数的实参个数大于形参个数时,我们可以将剩余的实参放到一个数组中。

传统写法

ES5 中,在定义方法时,参数要确定个数,如下:(程序会报错)

function fn(a, b, c) {
    console.log(a);
    console.log(b);
    console.log(c);
    console.log(d);
}

fn(1, 2, 3);

上方代码中,因为方法的参数是三个,但使用时是用到了四个参数,所以会报错。

20180304_1638.png

ES6 写法

ES6 中,我们有了剩余参数,就不用担心报错的问题了。代码可以这样写:

const fn = (...args) => {
    //当不确定方法的参数时,可以使用剩余参数
    console.log(args[0]);
    console.log(args[1]);
    console.log(args[2]);
    console.log(args[3]);
};

fn(1, 2);
fn(1, 2, 3); //方法的定义中了四个参数,但调用函数时只使用了三个参数,ES6 中并不会报错。

打印结果:

1
2
undefined
undefined


1
2
3
undefined

上方代码中注意,args 参数之后,不能再加别的参数,否则编译报错。

下面这段代码,也是利用到了剩余参数:

function fn1(first, ...args) {
    console.log(first); // 10
    console.log(args); // 数组:[20, 30]
}

fn1(10, 20, 30);

剩余参数的举例:参数求和

代码举例:

const sum = (...args) => {
    let total = 0;
    args.forEach(item => total += item); // 注意 forEach里面的代码,写得 很精简
    return total;
};
console.log(sum(10, 20, 30));

打印结果:60

剩余参数和解构赋值配合使用

代码举例:

const students = ['张三', '李四', '王五'];
let [s1, ...s2] = students;

console.log(s1); // '张三'
console.log(s2); // ['李四', '王五']

扩展运算符(展开语法)

扩展运算符和剩余参数是相反的。

剩余参数是将剩余的元素放到一个数组中;而扩展运算符是将数组或者对象拆分成逗号分隔的参数序列。

代码举例:

const arr = [10, 20, 30];
...arr // 10, 20, 30      注意,这一行是伪代码,这里用到了扩展运算符
console.log(...arr); // 10 20 30

console.log(10, 20, 30); // 10 20 30

上面的代码要仔细看:

arr是一个数组,而...arr则表示10, 20, 30这样的序列。

我们把...arr 打印出来,发现打印结果竟然是 10 20 30,为啥逗号不见了呢?因为逗号被当作了 console.log 的参数分隔符。如果你不信,可以直接打印 console.log(10, 20, 30) 看看。

接下来,我们看一下扩展运算符的应用。

举例1:数组赋值

数组赋值的代码举例:

let arr2 = [...arr1]; // 将 arr1 赋值给 arr2

为了理解上面这行代码,我们先来分析一段代码:(将数组 arr1 赋值给 arr2)

let arr1 = ['www', 'juejin', 'com'];
let arr2 = arr1; // 将 arr1 赋值给 arr2,其实是让 arr2 指向 arr1 的内存地址
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
console.log('---------------------');

arr2.push('青训营'); //往 arr2 里添加一部分内容
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);

上方代码中,我们往往 arr2 里添加了青训营,却发现,arr1 里也有这个内容。原因是:let arr2 = arr1;其实是让 arr2 指向 arr1 的地址。也就是说,二者指向的是同一个内存地址。

如果不想让 arr1 和 arr2 指向同一个内存地址,我们可以借助扩展运算符来做:

let arr1 = ['www', 'juejin', 'com'];
let arr2 = [...arr1]; //【重要代码】arr2 会重新开辟内存地址
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
console.log('---------------------');

arr2.push('青训营'); //往arr2 里添加一部分内容
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);

运行结果:

arr1:www,juejin,com
arr2:www,juejin,com
---------------------
arr1:www,juejin,com
arr2:www,juejin,com,青训营

我们明白了这个例子,就可以避免开发中的很多业务逻辑上的 bug。

举例2:合并数组

代码举例:

let arr1 = ['王一', '王二', '王三'];
let arr2 = ['王四', '王五', '王六'];
// ...arr1  // '王一','王二','王三'
// ...arr2  // '王四','王五','王六'

// 方法1
let arr3 = [...arr1, ...arr2];
console.log(arr3); // ["王一", "王二", "王三", "王四", "王五", "王六"]

// 方法2
arr1.push(...arr2);
console.log(arr1); // ["王一", "王二", "王三", "王四", "王五", "王六"]

举例3:将伪数组或者可遍历对象转换为真正的数组

代码举例:

const myDivs = document.getElementsByClassName('div');
const divArr = [...myDivs]; // 利用扩展运算符,将伪数组转为真正的数组

字符串、数组、对象的扩展

字符串的扩展

ES6 中的字符串扩展如下:

  • includes(str):判断是否包含指定的字符串
  • startsWith(str):判断是否以指定字符串开头
  • endsWith(str):判断是否以指定字符串结尾
  • repeat(count):重复指定次数

举例如下:

let str = 'abcdefg';

console.log(str.includes('a')); //true
console.log(str.includes('h')); //false

//startsWith(str) : 判断是否以指定字符串开头
console.log(str.startsWith('a')); //true
console.log(str.startsWith('d')); //false

//endsWith(str) : 判断是否以指定字符串结尾
console.log(str.endsWith('g')); //true
console.log(str.endsWith('d')); //false

//repeat(count) : 重复指定次数a
console.log(str.repeat(5));

20180402_1050.png

Number 的扩展

  • 二进制与八进制数值表示法: 二进制用0b, 八进制用0o

举例:

console.log(0b1010); //10
console.log(0o56); //46
  • Number.isFinite(i):判断是否为有限大的数。比如Infinity这种无穷大的数,返回的就是 false。
  • Number.isNaN(i):判断是否为 NaN。
  • Number.isInteger(i):判断是否为整数。
  • Number.parseInt(str):将字符串转换为对应的数值。
  • Math.trunc(i):去除小数部分。

举例:

//Number.isFinite(i) : 判断是否是有限大的数
console.log(Number.isFinite(NaN)); //false
console.log(Number.isFinite(5)); //true
console.log(Number.isFinite(Infinity)); //false

//Number.isNaN(i) : 判断是否是NaN
console.log(Number.isNaN(NaN)); //true
console.log(Number.isNaN(5)); //falsse

//Number.isInteger(i) : 判断是否是整数
console.log(Number.isInteger(5.23)); //false
console.log(Number.isInteger(5.0)); //true
console.log(Number.isInteger(5)); //true

//Number.parseInt(str) : 将字符串转换为对应的数值
console.log(Number.parseInt('123abc')); //123
console.log(Number.parseInt('a123abc')); //NaN

// Math.trunc(i) : 直接去除小数部分
console.log(Math.trunc(13.123)); //13

数组的扩展

  • Array.from()
  • find()
  • findIndex()

对象的扩展

扩展 1

Object.is(v1, v2);

作用:判断两个数据是否完全相等。底层是通过字符串来判断的。

我们先来看下面这两行代码的打印结果:

console.log(0 == -0);
console.log(NaN == NaN);

打印结果:

	true
	false

上方代码中,第一行代码的打印结果为 true,这个很好理解。第二行代码的打印结果为 false,因为 NaN 和任何值都不相等。

但是,如果换成下面这种方式来比较:

console.log(Object.is(0, -0));
console.log(Object.is(NaN, NaN));

打印结果却是:

	false
	true

代码解释:还是刚刚说的那样,Object.is(v1, v2)比较的是字符串是否相等。

Object.assign()

Object.assign() 在实战开发中,使用到的频率非常高,一定要重视。关于它的内容,详见《04-JavaScript 基础/浅拷贝和深拷贝.md》。

扩展 3:__proto__属性

举例:

let obj1 = { name: 'henry' };
let obj2 = {};

obj2.__proto__ = obj1;

console.log(obj1);
console.log(obj2);
console.log(obj2.name);

上方代码中,obj2 本身是没有属性的,但是通过__proto__属性和 obj1 产生关联,于是就可以获得 obj1 里的属性。

内置对象扩展:Set数据结构

Set 数据结构

Set 数据结构的介绍

ES6 提供了 新的数据结构 Set。Set 类似于数组,但成员的值都是唯一的,没有重复的值。

Set 的应用有很多。比如,在 H5 页面的搜索功能里,用户可能会多次搜索重复的关键字;但是在数据存储上,不需要存储重复的关键字。此时,我们就可以用 Set 来存储用户的搜索记录,Set 内部会自动判断值是否重复,如果重复,则不会进行存储,十分方便。

生成 Set 数据结构

Set 本身就是一个构造函数,可通过 new Set() 生成一个 Set 的实例。

举例 1:

const set1 = new Set();
console.log(set1.size); // 打印结果:0

举例 2、可以接收一个数组作为参数,实现数组去重

const set2 = new Set(['张三', '李四', '王五', '张三']); // 注意,这个数组里有重复的值

// 注意,这里的 set2 并不是数组,而是一个单纯的 Set 数据结构
console.log(set2); // {"张三", "李四", "王五"}

// 通过扩展运算符,拿到 set 中的元素(用逗号分隔的序列)
// ...set2 //  "张三", "李四", "王五"

// 注意,到这一步,才获取到了真正的数组
console.log([...set2]); // ["张三", "李四", "王五"]

注意上方的第一行代码,虽然参数里传递的是数组结构,但拿到的 set2 不是数组结构,而是 Set 结构,而且里面元素是去重了的。通过 [...set2]就可以拿到set2对应的数组。