ES6 经常使用方法概览

整理自阮一峰 ECMAScript 入门html

模板字符串

  1. 模板字符串(template string)是加强版的字符串,用反引号(`)标识。它能够看成普通字符串使用,也能够用来定义多行字符串,或者在字符串中嵌入变量。
  2. 模板字符串,都是用反引号表示。若是在模板字符串中须要使用反引号,则前面要用反斜杠转义。
  3. 若是使用模板字符串表示多行字符串,全部的空格和缩进都会被保留在输出之中。
  4. 全部模板字符串的空格和换行,都是被保留的,好比<ul>标签前面会有一个换行。若是你不想要这个换行,可使用trim方法消除它。
$('#list').html(`
<ul>
  <li>first</li>
  <li>second</li>
</ul>
`.trim());
复制代码

字符串新增方法

实例方法:includes(), startsWith(), endsWith()

  1. includes():返回布尔值,表示是否找到了参数字符串。
  2. startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  3. endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。 这三个方法都支持第二个参数,表示开始搜索的位置。
let s = 'Hello world!';

s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
复制代码

上面代码表示,使用第二个参数n时,endsWith的行为与其余两个方法有所不一样。它针对前n个字符,而其余两个方法针对从第n个位置直到字符串结束。node

实例方法:repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。es6

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
复制代码

实例方法:padStart(),padEnd()

ES2017 引入了字符串补全长度的功能。若是某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。 padStart()和padEnd()一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。正则表达式

'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
复制代码

padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。算法

'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"
复制代码

实例方法:trimStart(),trimEnd()

ES2019 对字符串实例新增了trimStart()和trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。编程

const s = ' abc ';

s.trim() // "abc"
s.trimStart() // "abc "
s.trimEnd() // " abc"
复制代码

实例方法:matchAll()

matchAll()方法返回一个正则表达式在当前字符串的全部匹配。 若是一个正则表达式在字符串里面有多个匹配,如今通常使用g修饰符或y修饰符,在循环里面逐一取出。ES6增长了String.prototype.matchAll()方法,能够一次性取出全部匹配。不过,它返回的是一个遍历器(Iterator),而不是数组。数组

const string = 'test1test2test3';

// g 修饰符加不加均可以
const regex = /t(e)(st(\d?))/g;

for (const match of string.matchAll(regex)) {
  console.log(match);
}
// ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
// ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
// ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]
复制代码

遍历器转为数组是很是简单的,使用...运算符和Array.from()方法就能够了。安全

// 转为数组方法一
[...string.matchAll(regex)]

// 转为数组方法二
Array.from(string.matchAll(regex))
复制代码

数值的扩展

二进制和八进制表示法

ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。bash

0b111110111 === 503 // true
0o767 === 503 // true
复制代码

若是要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。markdown

Number('0b111')  // 7
Number('0o10')  // 8
复制代码

扩展:Number 对象

Number对象能够将任何数据类型转换成数值,包括 布尔值,日期,二进制数,八进制数,数字字符串(parseInt,parseFloat只能够转化数字字符串)。

Number 对象的方法

  1. Number.isNaN() 肯定传递的值是不是 NaN。(ES6新增)
  2. Number.isFinite() 肯定传递的值类型及自己是不是有限数。(ES6新增)
  3. Number.isInteger() 肯定传递的值类型是“number”,且是整数。
  4. Number.isSafeInteger() 肯定传递的值是否为安全整数 ( -(253 - 1) 至 253 - 1之间)。
  5. Number.toInteger() 计算传递的值并将其转换为整数 (或无穷大)。
  6. Number.parseFloat() 和全局对象 parseFloat() 同样。
  7. Number.parseInt() 和全局对象 parseInt() 同样。

扩展:Math 对象

  1. Math.trunc 方法用于去除一个数的小数部分,返回整数部分。(对于非数值,会先使用Number将其转换为数值。)
  2. Math.sign方法用来判断一个数究竟是正数、负数、仍是零。共有五种返回值:+1,-1,+0,-0,NaN。(对于非数值,会先使用Number将其转换为数值。)
  3. Math.sinh(x) 返回x的双曲正弦(hyperbolic sine)
  4. Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine)
  5. Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)
  6. Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine)
  7. Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine)
  8. Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent)

扩展:指数运算符

指数运算符(**) 这个运算符的一个特色是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。 例如:

// 至关于 3 ** (3 ** 2)
3 ** 3 ** 2
// 19683
复制代码

指数运算符能够与等号结合,造成一个新的赋值运算符(**=)

let a = 1.5;
a **= 2;
// 等同于 a = a * a;

let b = 4;
b **= 3;
// 等同于 b = b * b * b;
复制代码

函数扩展

指定函数参数的默认值

ES6 容许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {
  console.log(x, y);
}
复制代码

tip:一般状况下,定义了默认值的参数,应该是函数的尾参数。由于这样比较容易看出来,到底省略了哪些参数。若是非尾部的参数设置默认值,实际上这个参数是无法省略的。

rest 参数

rest 参数与扩展运算符实际上是同一种东西,就是... 只不过使用场景不一样,他们的名字也不同。

  • 当用在函数定义时的形参前面时,称为rest参数,当函数调用时,用于接收不肯定的参数。例如:function sumRest (...m) {
  • 当与解构赋值组合使用时,称为rest参数,用于接收剩余的值。例如:{a,b,...m}
  • 当用在字符串或数组前面时称为扩展运算符,将数组或字符串进行拆解。例如:console.log(...array);

箭头函数

var f = v => v; 若是箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,而且使用return语句返回。 因为大括号被解释为代码块,因此若是箭头函数直接返回一个对象,必须在对象外面加上括号,不然会报错。

let getTempItem = id => ({ id: id, name: "Temp" });
复制代码

注意点 箭头函数体内的this对象,就是定义时所在的对象,这个this是固定的,注意它没有本身的this。

function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42
复制代码

上面代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到 100 毫秒后。若是是普通函数,执行时this应该指向全局对象window,这时应该输出21。可是,箭头函数致使this老是指向函数定义生效时所在的对象(本例是{id: 42}),因此输出的是42。 箭头函数转成 ES5 的代码以下:

// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}
复制代码

数组的扩展

扩展运算符的应用

(1)复制数组

const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
复制代码

(2)合并数组

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

复制代码

须要注意的是:这两种方法都是浅拷贝,使用的时候须要注意。合并而成的新数组,它们的成员都是对原数组成员的引用,这就是浅拷贝。若是修改了引用指向的值,会同步反映到新数组。

(5)实现了 Iterator 接口的对象

任何定义了遍历器(Iterator)接口的对象(参阅 Iterator 一章),均可以用扩展运算符转为真正的数组。

let nodeList = document.querySelectorAll('div');
let array = [...nodeList];
复制代码

上面代码中,querySelectorAll方法返回的是一个NodeList对象。它不是数组,而是一个相似数组的对象。这时,扩展运算符能够将其转为真正的数组,缘由就在于NodeList对象实现了 Iterator 。

数组实例的 flat(),flatMap()

数组的成员有时仍是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。

[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
复制代码

上面代码中,原数组的成员里面有一个数组,flat()方法将子数组的成员取出来,添加在原来的位置。

flat()默认只会“拉平”一层,若是想要“拉平”多层的嵌套数组,能够将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1。 若是flat()的参数为2,表示要“拉平”两层的嵌套数组。 若是无论有多少层嵌套,都要转成一维数组,能够用Infinity关键字做为参数。

[1, 2, [3, [4, 5]]].flat(2)
// [1, 2, 3, 4, 5]
[1, [2, [3]]].flat(Infinity)
// [1, 2, 3]
复制代码

对象的扩展

链判断运算符

编程实务中,若是读取对象内部的某个属性,每每须要判断一下该对象是否存在。好比,要读取message.body.user.firstName,安全的写法是写成下面这样。

const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
  
  或
  
const fooInput = myForm.querySelector('input[name=foo]')
const fooValue = fooInput ? fooInput.value : undefined
复制代码

这样的层层判断很是麻烦,所以 ES2020 引入了“链判断运算符”(optional chaining operator)?.,简化上面的写法。

const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value
复制代码

上面代码使用了?.运算符,直接在链式调用的时候判断,左侧的对象是否为null或undefined。若是是的,就再也不往下运算,而是返回undefined。

三种用法

链判断运算符有三种用法。

obj?.prop // 对象属性
obj?.[expr] // 同上
func?.(...args) // 函数或对象方法的调用
复制代码

下面是判断对象方法是否存在,若是存在就当即执行的例子。

iterator.return?.()
复制代码

上面代码中,iterator.return若是有定义,就会调用该方法,不然直接返回undefined。

对于那些可能没有实现的方法,这个运算符尤为有用。

if (myForm.checkValidity?.() === false) {
  // 表单校验失败
  return;
}
复制代码

注意点

(1). 短路机制

a?.[++x]
// 等同于
a == null ? undefined : a[++x]
复制代码

上面代码中,若是a是undefined或null,那么x不会进行递增运算。也就是说,链判断运算符一旦为真,右侧的表达式就再也不求值。 (2). delete 运算符

delete a?.b
// 等同于
a == null ? undefined : delete a.b
复制代码

(3). 括号的影响

若是属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。

(a?.b).c // 等价于 (a == null ? undefined : a.b).c 上面代码中,?.对圆括号外部没有影响,无论a对象是否存在,圆括号后面的.c老是会执行。

通常来讲,使用?.运算符的场合,不该该使用圆括号。

(4).报错场合

如下写法是禁止的,会报错。

// 构造函数
new a?.()
new a?.b()

// 链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`

// 链判断运算符的左侧是 super
super?.()
super?.foo

// 链运算符用于赋值运算符左侧
a?.b = c
复制代码

(5).右侧不得为十进制数值

为了保证兼容之前的代码,容许foo?.3:0被解析成foo ? .3 : 0,所以规定若是?.后面紧跟一个十进制数字,那么?.再也不被当作是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,造成一个小数。

Null 判断符

读取对象属性的时候,若是某个属性的值是null或undefined,有时候须要为它们指定默认值。常见作法是经过||运算符指定默认值。

const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;
复制代码

上面的三行代码都经过||运算符指定默认值,可是这样写是错的。开发者的原意是,只要属性的值为null或undefined,默认值就会生效,可是属性的值若是为空字符串或false或0,默认值也会生效。

为了不这种状况,ES2020 引入了一个新的 Null 判断运算符??。它的行为相似||,可是只有运算符左侧的值为null或undefined时,才会返回右侧的值。

const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;
复制代码

上面代码中,默认值只有在属性值为null或undefined时,才会生效。

这个运算符的一个目的,就是跟链判断运算符?.配合使用,为null或undefined的值设置默认值。

const animationDuration = response.settings?.animationDuration ?? 300;
复制代码

上面代码中,response.settings若是是null或undefined,就会返回默认值300。

Object.is()

ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺少一种运算,在全部环境中,只要两个值是同样的,它们就应该相等。

ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

Object.is('foo', 'foo')
// true
Object.is({}, {})
// false
复制代码

不一样之处只有两个:一是+0不等于-0,二是NaN等于自身。

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
复制代码

Object.keys(),Object.values(),Object.entries()

ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)全部可遍历(enumerable)属性的键名。

var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
复制代码

Object.values()

Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)全部可遍历(enumerable)属性的键值。

const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
复制代码

返回数组的成员顺序为:

  • 首先遍历全部数值键,按照数值升序排列。
  • 其次遍历全部字符串键,按照加入时间升序排列。
  • 最后遍历全部 Symbol 键,按照加入时间升序排列。

例如:

const obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj)
// ["b", "c", "a"]
复制代码

上面代码中,属性名为数值的属性,是按照数值大小,从小到大遍历的,所以返回的顺序是b、c、a。

规则以下:

  • Object.values会过滤属性名为 Symbol 值的属性。
  • 若是Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。
Object.values('foo')
// ['f', 'o', 'o']
复制代码
  • 若是参数不是对象,Object.values会先将其转为对象。因为数值和布尔值的包装对象,都不会为实例添加非继承的属性。因此,Object.values会返回空数组。
Object.values(42) // []
Object.values(true) // []
复制代码

Object.entries()

Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)全部可遍历(enumerable)属性的键值对数组。

const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
复制代码

除了返回值不同,该方法的行为与Object.values基本一致。

  • 用途1:Object.entries的基本用途是遍历对象的属性。
let obj = { one: 1, two: 2 };
for (let [k, v] of Object.entries(obj)) {
  console.log(
    `${JSON.stringify(k)}: ${JSON.stringify(v)}`
  );
}
// "one": 1
// "two": 2
复制代码
  • 用途2:Object.entries可将对象转为真正的Map结构。
const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }
复制代码

Object.fromEntries()

Object.fromEntries()方法是Object.entries()的逆操做,用于将一个键值对数组转为对象。

Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]
])
// { foo: "bar", baz: 42 }
复制代码

该方法的主要目的,是将键值对的数据结构还原为对象,所以特别适合将 Map 结构转为对象。

// 例一
const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);

Object.fromEntries(entries)
// { foo: "bar", baz: 42 }

// 例二
const map = new Map().set('foo', true).set('bar', false);
Object.fromEntries(map)
// { foo: true, bar: false }
复制代码

该方法的一个用处是配合URLSearchParams对象,将查询字符串转为对象。

Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }
复制代码

Set 和 Map 数据结构

Set

ES6 提供了新的数据结构 Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。 Set自己是一个构造函数,用来生成 Set 数据结构。 Set 接收一个数组或者类数组做为参数,用来初始化。

// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
// 可使用 Set 来给数组去重

// 例三
const set = new Set(document.querySelectorAll('div'));
set.size // 56
复制代码

上面的代码也展现了去除数组重复成员的方法

// 去除数组的重复成员
[...new Set(array)]
复制代码

也能够用来去除字符串里面的重复字符串

[...new Set('ababbc')].join('')
// "abc"
复制代码

Array.from 方法也能够将 Set结构转换为数组,这就提供了去重的另外一种方法

const items = new Set([1, 1, 2, 3, 3, 4, 5]);
const array = Array.from(items);
复制代码

Set 实例的属性和方法

1. size:返回 Set 实例的成员总数
2. add(value):添加某个值,返回Set结构自己
3. delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
4. has(value):返回一个布尔值,表示该值是否为Set的成员
5. clear():清除全部成员,没有返回值

遍历操做

Set 结构的四个遍历方法

1. keys():返回键名
2. values():返回键值
3. entries():返回键值对
4. forEach(): 使用回调函数遍历每一个成员
let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
复制代码

Map

Map 数据结构,它相似于对象,也是键值对的集合,可是“键”的范围不限于字符串,各类类型的值(包括对象)均可以看成键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。若是你须要“键值对”的数据结构,Map 比 Object 更合适。

Map 函数接收数组,或者类数组(每一个成员都是一个双元素的数组,看成Map构造函数的参数,这就是说,Set 和 Map 均可以用来生成新的 Map。

const set = new Set([
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1

const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3
复制代码

若是对同一个键屡次赋值,后面的值将覆盖前面的值。

const map = new Map();

map
.set(1, 'aaa')
.set(1, 'bbb');

map.get(1) // "bbb"
复制代码

注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要很是当心。

const map = new Map();

map.set(['a'], 555);
map.get(['a']) // undefined
复制代码

上面代码的set和get方法,表面是针对同一个键,但实际上这是两个不一样的数组实例,内存地址是不同的,所以get方法没法读取该键,返回undefined。

const map = new Map();
const list = ['a']
map.set(list, 555);
map.get(list) // 555
复制代码

将数组赋值给一个变量后,就能够获得想象中的结果了。

若是 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,好比0和-0就是一个键,布尔值true和字符串true则是两个不一样的键。另外,undefined和null也是两个不一样的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键

let map = new Map();

map.set(-0, 123);
map.get(+0) // 123

map.set(true, 1);
map.set('true', 2);
map.get(true) // 1

map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3

map.set(NaN, 123);
map.get(NaN) // 123
复制代码
相关文章
相关标签/搜索