重学ES6(一)

723 阅读3分钟

前言:本文旨在本人对ES6功能点的查漏补缺,以及ESn功能的用法做一下记录。主要参考阮一峰老师的《ECMAScript6入门》

let 和 const

for循环设置循环变量是一个副作用域,循环体内是单独的子作用域

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

es6的6种声明变量方法

var function let const import class

顶层对象

顶层对象在js中指window,在node中指global

var创建的属于顶层对象,let和const不属于

var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1

let b = 1;
window.b // undefined

es2020引入globalThis作为顶层对象。也就是说,任何环境下,globalThis都是存在的,都可以从它拿到顶层对象,指向全局环境下的this

字符串的扩展

标签模版

模版字符串可以跟在函数后面,这个函数会调用处理这个模板字符串。

let a = 5;
let b = 10;

tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);

简单理解下:

  • tag的第一个参数是数组,里面的元素是没有变量替换的部分,上述例子里就是hello和world
  • 其余的元素就是变量被替换的值,上述例子里就是a+b的结果和a*b的结果
  • 替换的规则是 数组的第一项元素Hello + 变量替换元素115 + 数组第二项元素world + 变量替换元素250,如果变量替换在模版字符串最后,那么数组会多加一项空元素

主要的作用:

  • 过滤 HTML 字符串,防止用户输入恶意内容
  • 国际化

这里不过多讨论具体用法,可以自行查阅

includes(), startsWith(), endsWith()

这三种方法都支持传入第二个参数,表示位置

let s = 'Hello world!';

s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true,针对前n个字符
s.includes('Hello', 6) // false

padStart(),padEnd()

第一个参数表示填充之后的长度

  • 参数小于字符本身的长度则无效

第二个参数表示填充的字符

  • 填充字符超出长度,则自动截断
  • 不传默认用空字符串填空
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

函数的扩展

length属性

函数的length属性,将返回没有指定默认值的参数个数

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

rest 参数

函数的arguments属性是类数组,使用时需要Array.from转一下,但是rest参数是真正的数组

// arguments变量的写法
function sortNumbers() {
  return Array.from(arguments).sort();
}

// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();

尾调用优化

尾调用(Tail Call)就是指某个函数的最后一步是调用另一个函数。

function f() {
  let m = 1;
  let n = 2;
  return g(m + n);
}
f();

// 等同于
function f() {
  return g(3);
}
f();

// 等同于
g(3);

尾调用优化只适用于,内部的函数不会使用外部函数的变量

function addOne(a){
  var one = 1;
  function inner(b){
    return b + one;
  }
  return inner(a); // 用到了外部函数变量one,所以不会进行尾调用优化
}

数组的扩展

Array.from

Array.from和扩展运算符的区别

扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。

Array.from方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。

Array.from({ length: 3 });
// [ undefined, undefined, undefined ]

Array.from可以接收第二个参数

效果等同于Array.from().map()

Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);

Array.of

第一次知道还有这个。。。

Array.of和Array的区别

// Array.of()总是返回参数值组成的数组。如果没有参数,就返回一个空数组
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of() // []
Array.of(undefined) // [undefined]

// Array()只有当参数个数不少于 2 个时,才会返回由参数组成的新数组。
// 参数只有一个正整数时,实际上是指定数组的长度。
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

includes()

indexOf的缺点:

1、找到的是下标,需和-1比较,不够直观

2、内部执行全等,所以不够准确

[NaN].indexOf(NaN) // -1

对象的扩展

Object.values()

如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。

Object.values('foo')
// ['f', 'o', 'o']

如果参数不是对象,Object.values会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以,Object.values会返回空数组。

Object.values(42) // []
Object.values(true) // []

由于数值和布尔值的包装对象,都不会为实例添加非继承的属性

这句话可以参考Object.keys,理解成该方法都会返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名,由于number和boolean类型都是不可遍历(enumerable)的,所以会返回空数组。实现方法大概是这样:

Object.values = Object.values || function values(object) {
  if (object === null || object === undefined) {
    throw new TypeError("Cannot convert undefined or null to object");
  }
  let result = [];
  if (isArrayLike(object) || isPlainObject(object)) {
    for (let key in object) {
      object.hasOwnProperty(key) && result.push(object[key]);
    }
  }
  return result;
};

参考自:Object.values:返回一个给定对象自身的所有可枚举属性值的数组

Object.entries()

用处:

将对象转成Map

const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }

Object.fromEntries()

用处:

1、将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 }

2、配合URLSearchParams处理url参数

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

运算符的扩展

Null 判断运算符??

||的不同是, ||判断的是左侧是falsy,??左侧必须是undefined或者null

const a = false || 1  // 1
const b = false ?? 1  // false

逻辑赋值运算符

// 或赋值运算符
x ||= y
// 等同于
x || (x = y)

// 与赋值运算符
x &&= y
// 等同于
x && (x = y)

// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)



// 老的写法
user.id = user.id || 1;

// 新的写法
user.id ||= 1;