ES6-10

定义

let

  1. 声明的变量具有全局作用域
  2. 不能使用全局对象的属性来访问
  3. 不能重复定义
  4. 不能进行变量声明提升

const

除了 let 的属性之外,它只能声明常量,不能被更改

Array

  • from用于转化伪数组,首先它按照索引方式传输数据,二是它有length属性,比如{0:'a',1:'b',length: 5}, 可以通过Array.from({length:3})直接创建一个长度为 3 的值为undefined的数组, 它还能接受一个函数,每次遍历都会执行,可以创建一个每个值为 1 的数组Array.from({length:3},function(){return 1})

  • of传入参数用于创建数组

  • fill填充数组Array(5).fill(1),第二个参数可以指定开始的索引,第三个参数可以指定结束的索引

let array = [1, 2, 3, 4, 5];
console.log(array.fill(8, 2, 4)); // [1, 2, 8, 8, 5]

includes

数组是否包含某个元素

flat(ES10)

可以按照可指定的深度进行扁平化, flatMap 相当于扁平化直接加上 map 操作

正则

  • y修饰符,用于保证字符串连续匹配,有点像是^

  • u修饰符,用于处理多个字节的,比如中文

let s = "吉";
let s2 = "\uD842\uDFB7";

console.log(/^\uD842/.test(s2)); // true
console.log(/^\uD842/u.test(s2)); // false
console.log(/吉{2}/u.test("吉吉")); // true
  • .在原来不能匹配一些字符,比如四个字符的 utf-16 字符,行中止符(回车,换行)。此时可以使用s修饰符,这样.就能匹配所有字符。使用RegExp.dotAll来判断是否开了dotAll模式,或者使用RegExp.flags来输出所有修饰符

  • 当使用局部匹配的时候,可以给每个匹配取名,通过这个名来访问。通过?<named>来定义,使用match后通过groups属性来访问

const t = "2019-06-07".match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
console.log(t.groups.year); // '2019'
  • 先行断言和后行断言,也就是根据前后字符串来进行匹配
let test = "hello world";
// 它会先匹配 hello 之后再看后面的内容是否匹配
console.log(test.match(/hello(?=\sworld)/));
// 检查前面的条件是否匹配
console.log(test.match(/(?<=hello\s)world/));
  • 在之前,exec方法只会匹配一次
let str = `"foo" and "bar" and "bar"`;

function select(regExp, str) {
  const matches = [];
  while (true) {
    const match = regExp.exec(str);
    if (match === null) break;
    matches.push(match[1]);
  }
  return matches;
}

console.log(select(/"([^"]*)"/g, str)); // ["foo", "bar", "bar"]

// 而 match 方法只会返回完整的匹配内容
str.match(/"([^"]*)"/g); // [""foo"", ""bar"", ""bar""]

// 使用 replace 方法也可以实现
function select(regExp, str) {
  const matches = [];
  str.replace(regExp, function (all, first) {
    matches.push(first);
  });
  return matches;
}
console.log(select(/"([^"]*)"/g, str)); // ["foo", "bar", "bar"]
// ES10
// string.matchAll 可以捕获所有的匹配元素
function select(regExp, str) {
  const matches = [];
  for (const match of str.matchAll(regExp)) {
    matches.push(match[1]);
  }
  return matches;
}

Reflect

反射,是在编译阶段不知道哪个类被加载,而是在运行的时候才加载

它是一个内置对象,它提供拦截 Javascript 操作的方法。这些方法与 proxy handlers 的方法相同。它不是一个函数对象,因此它是不可构造的,不能使用 new 进行构造,同时它的所有属性和方法都是静态的。现阶段这些方法存在于 Object 和 Reflect 对象上,未来只存在于 Reflect 对象上,也就是说,从 Reflect 对象上可以拿到语言内部的方法

它的意义有四个

  1. 从Reflect对象上拿到语言内部的方法
  2. 操作对象出现报错时返回 false
  3. 让操作对象都变为函数式编程
  4. 保持和 proxy 对象的方法——对象

Reflect 对象一共有 13 个静态方法。

  • Reflect.apply(target, thisArg, args)
  • Reflect.construct(target, args)
  • Reflect.get(target, name, receiver)
  • Reflect.set(target, name, value, receiver)
  • Reflect.defineProperty(target, name, desc)
  • Reflect.deleteProperty(target, name)
  • Reflect.has(target, name)
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

Proxy

中介能够很好的保证原始信息安全,第一个参数是代理对象,第二个是干什么,我们可以用它来进行一些字段的验证,比如邮箱或是电话号码,还可以进行监控用户操作,上报问题,将报错和上报结耦

// 设定对象只能读,不能写
const dog = {
    name: 'xiaoming',
    price: 190
}

// 使用 proxy,这里自己其实还是可以进行写操作
const d = new Proxy(o, {
    get(target, key) {
        return target[key]
    }
    set(target, key, value) {
        return false
    }
})

// ES5 自己都不能进行写操作,除非重新配置
for (let [key] of Object.entries(o)) {
    Object.defineProperty(o, key, {
        writable: false
    })
}

// 我们希望每个组件有自己的 ID,能够只能读
class Component {
    constructor() {
        // this.id = Math.random().toString(36).slice(-8)
        this.proxy = new Proxy({
            id: Math.random().toString(36).slice(-8)
        }, {})
    }

    get id() {
        return this.proxy.id
    }
}

使用 Proxy.revocable 方法可以用来创建一个可撤销的代理对象。该方法的返回值是一个对象,其结构为: {"proxy": proxy, "revoke": revoke} ,其中:

  • proxy

    表示新生成的代理对象本身,和用一般方式 new Proxy(target, handler) 创建的代理对象没什么不同,只是它可以被撤销掉。

  • revoke

    撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的那个代理对象。

一旦某个代理对象被撤销,它将变得几乎完全不可调用,在它身上执行任何的可代理操作都会抛出 TypeError 异常(注意,可代理操作一共有 14 种 ,执行这 14 种操作以外的操作不会抛出异常)。一旦被撤销,这个代理对象便不可能被直接恢复到原来的状态,同时和它关联的目标对象以及处理器对象都有可能被垃圾回收掉。再次调用撤销方法 revoke() 则不会有任何效果,但也不会报错。

Generator

迭代器通过生成器生成

function* gen() {
  let val;
  val = yield [1, 2, 3]; // val 的值为 [1, 2, 3]
  val = yield* [1, 2, 3]; // 会对数组进行迭代
  console.log(val);
}
// 年会抽奖
function draw(first = 1, second = 3, third = 5) {
  let firstPrize = ["1A", "1B", "1C", "1D", "1E"];
  let secondPrize = ["2A", "2B", "2C", "2D", "2E", "2F", "2G", "2H"];
  let thirdPrize = [
    "3A",
    "3B",
    "3C",
    "3D",
    "3E",
    "3F",
    "3G",
    "3H",
    "3I",
    "3J",
    "3K",
    "3L",
  ];
  let result = [];
  let random;

  function getPrize(luckyNum, prizeType) {
    for (let i = 0; i < luckyNum; i++) {
      // 随机抽取中奖者
      random = Math.floor(Math.random() * prizeType.length);
      // 把中奖者放入结果中并从一等奖中把奖励去除
      result = result.concat(prizeType.splice(random, 1));
    }
  }
  // 一等奖
  getPrize(first, firstPrize);
  getPrize(second, secondPrize);
  getPrize(third, thirdPrize);

  return result;
}

// ES6 使用 generator
function* draw(first = 1, second = 3, third = 5) {
  let firstPrize = ["1A", "1B", "1C", "1D", "1E"];
  let secondPrize = ["2A", "2B", "2C", "2D", "2E", "2F", "2G", "2H"];
  let thirdPrize = [
    "3A",
    "3B",
    "3C",
    "3D",
    "3E",
    "3F",
    "3G",
    "3H",
    "3I",
    "3J",
    "3K",
    "3L",
  ];
  let count = 0;
  let random;

  while (1) {
    if (count < first) {
      random = Math.floor(Math.random() * firstPrize.length);
      yield firstPrize[random];
      count++;
      firstPrize.splice(random, 1);
    } else if (count < first + second) {
      random = Math.floor(Math.random() * secondPrize.length);
      yield secondPrize[random];
      count++;
      secondPrize.splice(random, 1);
    } else if (count < first + second + third) {
      random = Math.floor(Math.random() * thirdPrize.length);
      yield thirdPrize[random];
      count++;
      thirdPrize.splice(random, 1);
    } else {
      return false;
    }
  }
}

let d = draw();
console.log(d.next().value);
console.log(d.next().value);
console.log(d.next().value);
// ...
// 将对象的遍历方法挂载到对象上,这样可增强维护性
let authors = {
  allAuthors: {
    fiction: ["Agla", "Skks", "LP"],
    scienceFiction: ["Neal", "Arthru", "Ribert"],
    fantasy: ["J.R.Tole", "J.M.R", "Terry P.K"],
  },
};

authors[Symbol.iterator] = function () {
  let allAuthors = this.allAuthors;
  let keys = Reflect.ownKeys(allAuthors);
  let values = [];
  // 必须返回一个对象,它必须有一个 next 方法,它也必须返回一个对象
  // 有 done 属性和 value 属性
  return {
    next() {
      if (!values.length) {
        if (keys.length) {
          values = allAuthors[keys[0]];
          keys.shift();
        }
      }
      return {
        done: !values.length,
        value: values.shift(),
      };
    },
  };
};

// 或者直接定义一个迭代器
authors[Symbol.iterator] = function* () {
  let allAuthors = this.allAuthors;
  let keys = Reflect.ownKeys(allAuthors);
  let values = [];
  while (1) {
    if (!values.length) {
      if (keys.length) {
        values = allAuthors[keys[0]];
        keys.shift();
        yield values.shift();
      } else {
        return false;
      }
    } else {
      yield values.shift();
    }
  }
};

let r = [];
for (let v of authors) {
  r.push(v);
}
console.log(r);

其他

  • 2 ** 3表示次方,原来是Math.pow(2, 3)

  • padStart字符串方法,接受两个参数,第一个为位数,第二个参数为如果位数不够而在从开头补充的字符串,相反效果的为padEnd

  • for await of用来进行异步逻辑的遍历

function Gen(timer) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(time);
    }, time);
  });
}

async function test() {
  for await (let item of arr) {
    console.log(Date.now(), item);
  }
}

test(); // 时间戳,异步结果
  • Promise的finally方法,用于兜底

  • JSON.stringify()对0xD800-0xDFFF之间的字符会因为无法编码成utf-8导致显示错误,ES10之后会将其以非编码的方式存在console.log(JSON.stringify('\u{D800}'))

  • string.trim(), string.trimStart(),string.trimLeft()和string.trimRight(),string.trimEnd()用于选择去除空格

  • Object.fromEntries可以将数组变为对象进行遍历

const arr1 = [
  ["foo", 1],
  ["bar", 2],
];
const obj = Object.fromEntries(arr);
console.log(obj.bar); // 2
  • BigInt用于处理大于2 ^ 52的数,只用在数字后面加n

  • 动态导入,现在导入可以分配给变量

elem.addEventListener("click", async () => {
  const module = await import(`./api-script/button-click.js`);
  module.clickEvent();
});
  • Symbol.description是一个只读属性,它返回Symbol对象的可选描述
let mySymbol = "My Symbol";
let symObj = Symbol(mySymbol);
symObj; // Symbol(My Symbol)
symObj.description; // 'My Symbol'
  • ES10 添加了globalThis对象,现在开始这个对象可以在任何平台访问全局作用域
// 类似于ES5 之前的 window.v = { flag: true }
globalThis.v = {
  flag: true,
};
console.log(globalThis.v); // {flog: true}
  • Function.toString()之前函数的toString方法是通过原型链查找到Object.toString(),而现在函数有自己的toString