30 советов по разработке ES6-ES12, которые вам нужно знать!

внешний интерфейс JavaScript ECMAScript 6
30 советов по разработке ES6-ES12, которые вам нужно знать!

Эта статья участвовала в "Проект «Звезда раскопок»”, чтобы выиграть творческий подарочный пакет и бросить вызов творческим поощрительным деньгам.


Это еще одна веселая еда и еще одна длинная статья на 10 000 символов. Я реорганизовал общие новые функции ES6-ES12. Многие функции по-прежнему очень полезны в разработке. Надеюсь, это поможет вам немного! Содержание статьи больше, рекомендуется собрать перед изучением!

ECMAScriptЯвляется языком программирования сценариев, стандартизированным Ecma International через ECMA-262, этот язык называется JavaScript. Проще говоря,ECMAScript — это стандарт и спецификация JavaScript, а JavaScript — это реализация и расширение стандарта ECMAScript.

С 2015 года ECMAScript выпускается в следующих версиях:

время выпуска официальное имя название версии Сокращенное название
2015 ECMAScript2015 ECMAScript6 ЕС2015, ЕС6
2016 ECMAScript2016 ECMAScript7 ЕС2016, ЕС7
2017 ECMAScript2017 ECMAScript8 ЕС2017, ЕС8
2018 ECMAScript2018 ECMAScript9 ЕС2018, ЕС9
2019 ECMAScript2019 ECMAScript10 ЕС2019, ЕС10
2020 ECMAScript2020 ECMAScript11 ЕС2020, ЕС11
2021 ECMAScript2021 ECMAScript12 ЕС2021, ЕС12

Давайте посмотрим на навыки использования каждой версии ECMAScript.ECMAScript.png**Примечание.** Некоторые сведения были представлены в предыдущих статьях и не будут повторяться в этой статье. К статье приложены ссылки на соответствующие статьи.

1. Новые возможности ES6 (2015)

Обновления ES6 в основном отражены в следующих аспектах:

  • Выражения: объявление переменной, деструктурирующее присваивание
  • Встроенные объекты: расширение строки, числовое расширение, расширение объекта, расширение массива, расширение функции, обычное расширение, символ, набор, карта, прокси, отражение
  • Заявления и операции: класс, модуль, итератор
  • Асинхронное программирование: Promise, Generator, Async.

Вот некоторые часто используемые новые функции. Есть также некоторые функции, которые были представлены в предыдущих статьях, поэтому я не буду рассказывать здесь больше, просто свяжу их напрямую:

1. пусть и константа

В ES6 были добавлены ключевые слова let и const, где let в основном используется для объявления переменных, а const обычно используется для объявления констант. По сравнению с ключевым словом var, let и const имеют следующие характеристики:

характеристика var let const
переменное продвижение ✔️ × ×
глобальная переменная ✔️ × ×
Повторите заявление ✔️ × ×
переназначить ✔️ ✔️ ×
Временная мертвая зона × ✔️ ✔️
область действия блока × ✔️ ✔️
Только объявить без инициализации ✔️ ✔️ ×

Вот четыре из них:

(1) Переназначение

Переменные, объявленные с помощью ключевого слова const, являются «неизменяемыми». На самом деле константа гарантирует не то, что значение переменной нельзя изменить, а то, что адрес памяти, на который указывает переменная, не может быть изменен. Для базовых типов данных (числа, строки, логические значения) значение сохраняется по адресу памяти, на который указывает переменная, поэтому оно эквивалентно константе. Но для данных ссылочного типа (в основном объекты и массивы) переменная указывает на адрес памяти данных, и сохраняется только указатель, const может гарантировать только то, что указатель неизменен, а структура данных, на которую он указывает, неуправляема.

(2) Область действия на уровне блоков

До введения let и const не существовало области видимости на уровне блоков, что приводило к множеству проблем, таких как внутренние переменные, перезаписывающие внешние одноименные переменные:

var a = 1;
if (true) {
  var a = 2;
}

console.log(a);   // 输出结果:2 

Переменные цикла просачиваются как глобальные переменные:

var arr = [1, 2, 3];
for (var i = 0; i < arr.length; i++) {
  console.log(arr[i]);  // 输出结果:1  2  3
}

console.log(i); // 输出结果:3

Переменные, определенные с помощью let и const, имеют область действия на уровне блоков, поэтому описанные выше проблемы не возникнут:

let a = 1;
if (true) {
  let a = 2;
}

console.log(a); // 输出结果:1

const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);  // 输出结果:1  2  3
}

console.log(i); // Uncaught ReferenceError: i is not defined

(3) Вариативное продвижение

Мы знаем, что до ES6 существовало продвижение переменных, так называемое продвижение переменных означает, что переменные можно использовать до того, как они будут объявлены:

console.log(a); // 输出结果:undefined
var a = 1;

Суть продвижения переменных заключается в том, что движок JavaScript будет компилировать и анализировать код перед его выполнением.На этом этапе обнаруженные переменные и объявления функций будут добавлены в память, называемую лексическим окружением в движке JavaScript.и присвоить значение инициализации undefined. Затем войдите в фазу выполнения кода. Таким образом, JS-движок уже знает объявленные переменные и функции до выполнения кода. ​

Это явление не соответствует нашей интуиции, поэтому в ES6 ключевые слова let и const ограничивают продвижение переменных, переменные, определенные с помощью let, не инициализируются в значение undefined после добавления в лексическое окружение, а движок JS будет выполнять его только до инициализации. выполняется только во время лексического объявления и присваивания. И в промежутке времени между созданием переменной и фактической инициализацией к ним нельзя получить доступ или использовать, ES6 называет этоВременная мертвая зона:

// 暂时性死区 开始
a = "hello";     //  Uncaught ReferenceError: Cannot access 'a' before initialization

let a;   
//  暂时性死区 结束
console.log(a);  // undefined

(4) Повторное заявление

До ES6 переменные, объявленные с помощью ключевого слова var, не имели ограничений на повторное объявление переменных в области видимости, и можно было объявлять даже переменные с тем же именем, что и у параметров.Следующие две функции не будут сообщать об ошибке:

function funcA() {
  var a = 1;
  var a = 2;
}

function funcB(args) {
  var args = 1; 
}

И пусть исправит этот расхлябанный дизайн:

function funcA() {
  let a = 1;
  let a = 2;  // Uncaught SyntaxError: Identifier 'a' has already been declared
}

function funcB(args) {
  let args = 1;  // Uncaught SyntaxError: Identifier 'args' has already been declared
}

Теперь мы полностью отказались от var в нашем проекте и используем let для определения переменных и const для определения констант. В ESlint включены следующие правила:

"no-var": 0;

2. Разрушающее назначение

В ES6 также представлена ​​концепция деструктурирующего присваивания, которое следует за «сопоставлением шаблонов», то есть, пока шаблоны по обе стороны от знака равенства равны, переменной слева будет присвоено соответствующее значение. Разные типы данных деконструируются по-разному Давайте рассмотрим методы деконструкции разных типов данных. ​

Обычно в разработке я в основном используюНазначение деструктуризации объекта, такие как деконструкция значения porps в React и т. д., использование деструктурирующего присваивания для получения значения из родительского компонента, useState в React Hooks использует деструктурирующее присваивание массива;

(1) Деструктуризация массива

Все структуры данных с интерфейсом Iterator могут использовать деструктурирующее присваивание в виде массива.

const [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(foo, bar, baz) // 输出结果:1  2  3

Здесь ES6 реализует структуру массива и по очереди назначает переменные foo, bar и baz. Назначение деструктурирования массива сопоставляет значения с переменными по положению. ​

Массивы также могут обеспечить неполную деструктуризацию, только деструктурируя часть содержимого:

const [x, y] = [1, 2, 3];   // 提取前两个值
const [, y, z] = [1, 2, 3]  // 提取后两个值
const [x, , z] = [1, 2, 3]  // 提取第一三个值

Если при деструктуризации в соответствующей позиции нет значения, переменной будет присвоено значение undefined:

const [x, y, z] = [1, 2]; 
console.log(z)  // 输出结果:undefined

Назначение деструктурирования массива может использовать оператор rest для захвата остатка:

const [x, ...y] = [1, 2, 3];   
console.log(x);  // 输出结果:1
console.log(y);  // 输出结果:[2, 3]

Значение по умолчанию также поддерживается во время деструктуризации, и значение по умолчанию будет использоваться, когда соответствующее значение не определено:

const [x, y, z = 3] = [1, 2]; 
console.log(z)  // 输出结果:3

(2) Деструктуризация объекта

Суть присваивания деструктуризации объекта состоит в том, чтобы сначала найти свойство с таким же именем, а затем присвоить его соответствующей переменной:

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
console.log(foo, bar); // 输出结果:aaa  bbb

Важно отметить, что в JavaScript свойства объекта не упорядочены. Поэтому при деструктурировании присваивания переменная должна иметь то же имя, что и свойство, чтобы получить значение. ​

Деструктурирующее присвоение объектов также поддерживает значения по умолчанию.Если определенная переменная не существует в объекте, вступит в силу ее значение по умолчанию:

let { foo, bar, baz = 'ccc'} = { foo: 'aaa', bar: 'bbb', baz: null };
console.log(foo, bar, baz); // 输出结果:aaa  bbb  null

let { foo, bar, baz = 'ccc'} = { foo: 'aaa', bar: 'bbb' };
console.log(foo, bar, baz); // 输出结果:aaa  bbb  ccc

Как видите, его значение по умолчанию вступит в силу только в том случае, если определенная переменная строго === не определена.

Кроме того, мы также должны отметить, что мы не можем присваивать значения объявленным переменным, потому что, когда ключевые слова let, const и var отсутствуют, {baz} будет пониматься как блок кода, что приведет к синтаксической ошибке, поэтому следующий код будет ошибкой:

let baz;
{ baz } = { foo: 'aaa', bar: 'bbb', baz: 'ccc' };

Вышеупомянутую проблему можно решить, заключив весь оператор присваивания деструктурирования в круглые скобки:

let baz;
({ baz } = { foo: 'aaa', bar: 'bbb', baz: 'ccc' });
console.log(baz)

При назначении деструктуризации объекта вы можете назначить метод существующего объекта переменной, например:

let { log, sin, cos } = Math;
log(12)  // 输出结果:2.4849066497880004
sin(1)   // 输出结果:0.8414709848078965
cos(1)   // 输出结果:0.5403023058681398

(3) Другие задания по деструктуризации

Остальные типы заданий деструктурирования, которые я сейчас меньше использую в проекте, давайте кратко рассмотрим. ​

  • Деструктуризация строки

Правила деструктурирования строк: если значение справа от знака равенства не является объектом или массивом, сначала преобразуйте его в объект, подобный массиву, а затем деконструируйте его:

const [a, b, c, d, e] = 'hello';
console.log(a, b, c, d, e)  // 输出结果:h e l l o

Объекты, подобные массивам, имеют свойство длины, поэтому этому свойству можно выполнять деструктурирующие присваивания:

let {length} = 'hello';    // 输出结果: 5

Поскольку строка является константой, мы обычно знаем, каково ее значение, поэтому мы редко используем деструктурирующее присваивание переменной.

  • Назначение числовой и логической деструктуризации

При деструктурировании чисел и логических значений они преобразуются в объекты перед применением синтаксиса деструктурирования:

let {toString: s} = 123;
s === Number.prototype.toString // 输出结果:true

let {toString: s} = true;
s === Boolean.prototype.toString // 输出结果:true

Обратите внимание, что null и undefined не могут быть преобразованы в объекты, поэтому, если в правой части находятся эти два значения, будет сообщено об ошибке. ​

  • Назначение деструктурирования параметра функции

Параметр функции якобы является массивом, который разбивается на x и y в момент передачи параметра.

function add([x, y]){
  return x + y;
}
add([1, 2]);   // 3

В дополнение к этому мы также можем деструктурировать возвращаемое значение функции:

function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();

3. Строки шаблона

В традиционном языке JavaScript выходной шаблон часто используется в виде сращивания строк, что довольно громоздко, и для решения вышеуказанных проблем в ES6 введено понятие шаблонных строк.

Строки шаблона — это расширенные строки, помеченные обратными кавычками ``. Их можно использовать для определения однострочных строк, многострочных строк или для встраивания переменных в строки.

// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

// 字符串中调用函数
` ${fn()} 

В обычной разработке, в дополнение к приложению в приведенном выше коде, строки шаблона используются во многих местах, таких как объединение строки DOM, определение структуры DOM в Emotion/styled и т. д., будут использоваться строки шаблона. Однако при определении элементов DOM в строках шаблона подсказок по коду нет. ​

При использовании строк шаблона следует помнить о нескольких вещах:

  • Если вы используете обратные кавычки в строке, вам нужно использовать \ для выхода;
  • Если в многострочной строке есть пробелы и отступы, они сохраняются в выводе;
  • Чтобы встроить переменные в строку шаблона, вам нужно написать имя переменной в ${};
  • В строки шаблона можно помещать произвольные выражения, также можно выполнять операции, можно ссылаться на свойства объектов и даже вызывать функции;
  • Если переменная в символе шаблона не объявлена, будет сообщено об ошибке.

4. Параметры функции по умолчанию

До ES6 функции не поддерживали параметры по умолчанию, ES6 реализует поддержку для этого и запускает значения по умолчанию только тогда, когда никакие параметры не переданы:

function getPoint(x = 0, y = 0) {
  console.log(x, y);
}

getPoint(1, 2);   // 1  2
getPoint()        // 0  0 
getPoint(1)       // 1  0

При использовании функций по умолчанию следует помнить о нескольких вещах:

(1) Значение атрибута длины функции

Атрибут длины функции обычно используется для указания количества параметров функции.Когда вводится значение функции по умолчанию, длина указывает количество общих параметров перед первым параметром со значением по умолчанию:

const funcA = function(x, y) {};
console.log(funcA.length);  // 输出结果:2 

const funcB = function(x, y = 1) {};
console.log(funcB.length);  // 输出结果:1

const funcC = function(x = 1, y) {};
console.log(funcC.length);  // 输出结果 0 

(2) Область параметров

Когда для параметров функции установлены значения по умолчанию, параметры будут формировать независимую область видимости при их инициализации, и область видимости будет разрешена после завершения инициализации:

let x = 1;

function func(x, y = x) {
  console.log(y);
}

func(2);  

Это в конечном итоге напечатает 2. Когда функция вызывается, параметры x, y образуют отдельную область видимости, поэтому y в параметре будет равно x в первом параметре, а не 1, определенному выше.

5. Стрелочные функции

Стрелочные функции были введены в ES6 для упрощения определения функций:

const counter = (x, y) => x + y;

По сравнению с обычными функциями стрелочные функции имеют следующие характеристики:

(1) Более кратко

  • Если параметров нет, просто напишите пустую скобку
  • Если параметр только один, круглые скобки параметра можно опустить.
  • Если параметров несколько, разделите их запятыми
  • Если возвращаемое значение тела функции представляет собой только одно предложение, фигурные скобки можно опустить.
// 1. 不传入参数
const funcA = () => console.log('funcA');
// 等价于
const funcA = function() {
  console.log('funcA');
} 

// 2. 传入参数
const funcB = (x, y) => x + y;
// 等价于
const funcB = function(x, y) {
  return x + y;
} 

// 3. 单个参数的简化
const funcC = (x) => x;
// 对于单个参数,可以去掉 (),简化为
const funcC = x => x;
// 等价于
const funcC = function(x) {
  return x;
}

// 4. 上述代码函数体只有单条语句,如果有多条,需要使用 {}
const funcD = (x, y) => { console.log(x, y); return x + y; }
// 等价于
const funcD = function(x, y) {
  console.log(x, y);
  return x + y;
}

(2) не связывайте это

Стрелочная функция не создает собственного this, поэтому у нее нет собственного this, она просто наследует это на один уровень выше своей области видимости. Таким образом, указатель this в функции стрелки определяется, когда он определен, и после этого не изменится.

var id = 'GLOBAL';
var obj = {
  id: 'OBJ',
  a: function(){
    console.log(this.id);
  },
  b: () => {
    console.log(this.id);
  }
};
obj.a();    // 'OBJ'
obj.b();    // 'GLOBAL'
new obj.a()  // undefined
new obj.b()  // Uncaught TypeError: obj.b is not a constructor

Метод b объекта obj определяется с помощью функции стрелки. this в этой функции всегда указывает на this в глобальной среде выполнения, где он определен. Даже если функция вызывается как метод объекта obj, это все равно указывает к объекту окна. Обратите внимание, что фигурные скобки, определяющие объект{}Невозможно сформировать отдельную среду выполнения, она все равно находится в глобальной среде выполнения.

Точно так же использование call(), apply(), bind() и других методов не может изменить указатель this в стрелочной функции:

var id = 'Global';
let fun1 = () => {
    console.log(this.id)
};
fun1();                     // 'Global'
fun1.call({id: 'Obj'});     // 'Global'
fun1.apply({id: 'Obj'});    // 'Global'
fun1.bind({id: 'Obj'})();   // 'Global'

(3) Недоступно в качестве конструктора

Шаги выполнения оператора new конструктора следующие:

  1. создать объект
  2. Назначьте область конструктора новому объекту (то есть укажите свойство __proto__ объекта на свойство прототипа конструктора)
  3. Указатель на код в конструкторе, this в конструкторе указывает на объект (то есть добавление свойств и методов к этому объекту)
  4. вернуть новый объект

По сути, второй шаг — указать this в функции на объект. Однако, поскольку стрелочные функции не имеют собственного this, а this указывает на внешнюю среду выполнения и не может быть изменено, его нельзя использовать в качестве конструктора.

(4) Не связывайте аргументы

Стрелочные функции не имеют собственного объекта аргументов. Доступ к аргументам в стрелочной функции фактически получает значение аргументов ее внешней функции.

6. Оператор спреда

Оператор расширения: ... подобен операции, обратной оставшемуся параметру, превращая массив в последовательность параметров, разделенных запятыми, и распаковывая массив.

Оператор спреда имеет следующий видиспользовать:

(1) Преобразуйте массив в последовательность параметров, разделенных запятыми:

function  test(a,b,c){
    console.log(a); // 1
    console.log(b); // 2
    console.log(c); // 3
}

var arr = [1, 2, 3];
test(...arr);

(2) Объединить один массив с другим:

var arr1 = [1, 2, 3,4];
var arr2 = [...arr1, 4, 5, 6];
console.log(arr2);  // [1, 2, 3, 4, 4, 5, 6]

(3) Преобразуйте строку в массив, разделенный запятыми:

var str='JavaScript';
var arr= [...str];
console.log(arr); // ["J", "a", "v", "a", "S", "c", "r", "i", "p", "t"]

7. Symbol

Новый примитивный тип данных Symbol был введен в ES6 для представления уникальных значений. Это строковый тип данных со следующими характеристиками:

  • Значение Symbol уникально и используется для решения проблемы конфликтов имен.
  • Значения символов не могут работать с другими типами данных
  • Свойства объекта, определенные символом, не могут быть использованыfor...inпройти цикл, но вы можете использоватьReflect.ownKeysчтобы получить все ключевые имена объекта
let s1 = Symbol();
console.log(typeof s1); // "symbol"

let s2 = Symbol('hello');
let s3 = Symbol('hello');
console.log(s2 === s3); // false

Исходя из вышеперечисленных характеристик, тип атрибута Symbol больше подходит для двух типов сценариев:Постоянные значения и свойства объекта.

(1) Избегайте повторения постоянных значений

Функция getValue выполнит соответствующую логику кода в соответствии с ключом параметра входящей строки:

function getValue(key) {
  switch(key){
    case 'A':
      ...
    case 'B':
      ...
  }
}
getValue('B');

Этот код очень недружественный для вызывающей стороны, потому что в коде используется волшебная строка (волшебная строка, которая ссылается на определенную строку или значение, которое появляется в коде много раз и образует сильную связь с кодом), что приводит к необходимости смотреть на код функции для поиска необязательного значения ключа параметра при вызове функции getValue. Таким образом, вы можете объявить значение ключа параметра константой:

const KEY = {
  alibaba: 'A',
  baidu: 'B',
}
function getValue(key) {
  switch(key){
    case KEY.alibaba:
      ...
    case KEY.baidu:
      ...
  }
}
getValue(KEY.baidu);

Но это не идеально. Предположим, вы хотите добавить ключ к константе KEY. Согласно соответствующим правилам, очень вероятно, что значение будет повторяться:

const KEY = {
  alibaba: 'A',
  baidu: 'B',
  tencent: 'B'
}

Вот проблема:

getValue(KEY.baidu) // 等同于 getValue(KEY.tencent)

Таким образом, в этом сценарии лучше использовать Symbol, вам не нужно заботиться о самом значении, а только об уникальности значения:

const KEY = {
  alibaba: Symbol(),
  baidu: Symbol(),
  tencent: Symbol()
}

(2) Избегайте перезаписи свойств объекта

Функция fn должна добавить к входящему параметру объекта временный атрибут user, но параметр объекта может уже иметь этот атрибут, и если он назначен напрямую, то перезапишет предыдущее значение. На этом этапе вы можете использовать Symbol, чтобы избежать этой проблемы. Создайте переменную типа данных Symbol, а затем назначьте и прочитайте переменную как свойство параметра объекта, чтобы избежать перезаписи:

function fn(o) { // {user: {id: xx, name: yy}}
  const s = Symbol()
  o[s] = 'zzz'
}

8. Набор для сбора

ES6 предоставляет новую структуру данных Set (коллекцию). Он похож на массив, но значения всех элементов уникальны, а коллекция реализует интерфейс итератора, поэтому ее можно пройти с помощью оператора распространения и for...of.

Установите свойства и методы:

свойства и методы Обзор
size Возвращает количество элементов в коллекции
add Добавляет новый элемент и возвращает текущую коллекцию
delete удалить элемент, вернуть логическое значение
has Проверяет, содержит ли коллекция элемент, возвращает логическое значение
clear Очистить коллекцию, вернуть undefined
//创建一个空集合
let s = new Set();
//创建一个非空集合
let s1 = new Set([1,2,3,1,2,3]);
//返回集合的元素个数
console.log(s1.size);       // 3
//添加新元素
console.log(s1.add(4));     // {1,2,3,4}
//删除元素
console.log(s1.delete(1));  //true
//检测是否存在某个值
console.log(s1.has(2));     // true
//清空集合
console.log(s1.clear());    //undefined

Благодаря уникальности элементов в наборе, в практических приложениях можно использовать набор для дедупликации массива:

let arr = [1,2,3,2,1]
Array.from(new Set(arr))  // {1, 2, 3}

Здесь метод Array.form() используется для преобразования набора массивов в массив. ​

Вы можете использовать set, чтобы найти пересечение и объединение двух массивов:

// 模拟求交集 
let intersection = new Set([...set1].filter(x => set2.has(x)));

// 模拟求差集
let difference = new Set([...set1].filter(x => !set2.has(x)));

Массивы и коллекции могут быть преобразованы друг в друга следующими способами:

// Set集合转化为数组
const arr = [...mySet]
const arr = Array.from(mySet)

// 数组转化为Set集合
const mySet = new Set(arr)

9. Map

ES6 предоставляет структуру данных Map, которая аналогична объекту и также представляет собой набор команд ключ-значение, но диапазон ее значений ключей не ограничивается строками, и может быть значениями любого типа (в т.ч. То есть структура Object обеспечивает соответствие "строка-значение", структура Map обеспечивает соответствие "значение-значение", что является более полной реализацией структуры Hash. Если вам нужна структура данных "ключ-значение", Map больше подходит, чем Object. Map также реализует интерфейс итератора, поэтому его можно пройти с помощью оператора распространения и for…of .

Свойства и методы карты:

свойства и методы Обзор
size Возвращает количество элементов на карте
set Добавить новый элемент и вернуть текущую карту
get Возвращает значение ключа объекта имени ключа
has Проверьте, содержит ли карта элемент, верните логическое значение
clear Очистить карту, вернуть undefined
//创建一个空 map
let m = new Map();
//创建一个非空 map
let m2 = new Map([
 ['name', 'hello'],
]);
//获取映射元素的个数
console.log(m2.size);          // 1
//添加映射值
console.log(m2.set('age', 6)); // {"name" => "hello", "age" => 6}
//获取映射值
console.log(m2.get('age'));    // 6
//检测是否有该映射
console.log(m2.has('age'));    // true
//清除
console.log(m2.clear());       // undefined

Обратите внимание, что только ссылки на один и тот же объект обрабатываются структурой Map как один и тот же ключ:

let map = new Map(); 
map.set(['a'], 555); 
map.get(['a']) // undefined

Методы set и get приведенного выше кода кажутся для одного и того же ключа, но на самом деле это два значения, а адреса памяти разные, поэтому метод get не может прочитать ключ, поэтому он вернет значение undefined. ​

Из вышеизложенного видно, что ключ карты фактически привязан к адресу памяти, и пока адреса памяти разные, он рассматривается как два ключа. Это решает проблему конфликта атрибутов с одинаковыми именами.При расширении библиотеки, если вы используете объект в качестве имени ключа, вам не нужно беспокоиться о том, что ваш собственный атрибут имеет то же имя, что и исходный атрибут.

Если ключ Карты является значением простого типа (число, строка, логическое значение), Карта обрабатывает его как ключ, включая 0 и -0, пока два значения строго равны. Кроме того, хотя NaN не является строго равным самому себе, Map рассматривает его как один и тот же ключ.

let map = new Map(); 
map.set(NaN, 123); 
map.get(NaN) // 123 
map.set(-0, 123); 
map.get(+0) // 123 

10. Модульный

Спецификация модульной разработки ES Module была впервые представлена ​​в ES6, что позволило Javascript впервые поддерживать нативную модульную разработку. Модуль ES рассматривает файл как модуль, и каждый модуль имеет свою независимую область видимости, так как же подключить каждый модуль? Основным моментом является импорт и экспорт модулей.

(1) модуль экспорта экспорта

  • Обычный экспорт:
// 方式一
export var first = 'test';
export function func() {
    return true;
}

// 方式二
var first = 'test';
var second = 'test';
function func() {
    return true;
}
export {first, second, func};
  • как ключевое слово:
var first = 'test';
export {first as second};

Ключевое слово as позволяет переименовать открытую переменную или метод.После переименования одна и та же переменная может отображаться несколько раз.

  • export default

export default будет экспортировать вывод по умолчанию, то есть пользователю не нужно знать имя вывода в модуле, и он может указать для него любое имя при импорте.

// 导出
export default function () {
  console.log('foo');
}
// 导入
import customName from './export-default';

Уведомление:Скобки не требуются при импорте модуля по умолчанию, а переменная или метод, который экспортирует модуль по умолчанию, может иметь имя, но не имеет внешнего эффекта. экспорт по умолчанию можно использовать только один раз.

(2) модуль импорта импорта

  • Обычный импорт:
import {firstName, lastName, year} from './profile';

Расположение импортируемого модуля может быть относительным или абсолютным путем..js можно не указывать.Если путь представляет собой просто имя модуля, вам нужно указать движку, где просматривать файл конфигурации.

  • как ключевое слово:
import { lastName as surname } from './profile';

Команда импорта вынесена в голову модуля, поэтому не так важно, где она прописана, но выражения и переменные для импорта использовать нельзя.

  • Загрузить весь модуль (без вывода)
import 'lodash'; //仅仅是加载而已,无法使用
  • Загрузите весь модуль (с выводом)
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));

Уведомление:import * игнорирует вывод по умолчанию

(3) Использование импорта и экспорта соединений

  • Сначала импортируйте, затем экспортируйте
export { foo, bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';
export { foo, boo};
  • Сначала импортируется все, а затем выводится и по умолчанию
// 整体输出
export * from 'my_module';
// 导出default,正如前面所说,export default 其实导出的是default变量
export { default } from 'foo';
// 具名接口改default
export { es6 as default } from './someModule';

(4) Наследование модулей

export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
  return Math.exp(x);
}

Уведомление:экспорт * будет игнорировать значение по умолчанию.

Во-вторых, новые функции ES7 (2016 г.)

1. Array.prototype.includes

includes()Метод используется для определения того, содержит ли массив указанное значение, и возвращает true, если он есть, и false в противном случае. Этот метод не изменяет исходный массив. Его синтаксис следующий:

arr.includes(searchElement, fromIndex)

Метод имеет два параметра:

  • searchElement: Требуется, значение элемента для поиска.
  • fromIndex: необязательно, начните поиск целевого значения с индекса fromIndex. Если отрицательный, поиск начинается с индекса массива.length + fromIndex в порядке возрастания (даже при переходе вперед по абсолютному значению индексов fromIndex с конца, а затем поиске назад). По умолчанию 0.
[1, 2, 3].includes(2);  //  true
[1, 2, 3].includes(4);  //  false
[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true

До ES7 было принято использовать indexOf, чтобы определить, содержит ли массив указанное значение. Однако indexOf не является семантически ясным и интуитивным, а indexOf использует === внутренне для оценки равенства, поэтому имеет место неправильное суждение о NaN и включает исправления этой проблемы:

[1, 2, NaN].indexOf(NaN);   // -1
[1, 2, NaN].includes(NaN);  //  true

Примечание. При сравнении строк и символов с помощью include() учитывается регистр.

2. Экспоненциальный оператор

ES7 также представил оператор экспоненты, чтобы сделать вычисления экспоненты более удобными, что эквивалентно Math.pow() :

Math.pow(2, 10));  // 1024
2**10;           // 1024

3. Новые возможности ES8 (2017)

ES8 представляет async/await, решение для асинхронных функций, которое здесь не будет представлено. См. статью:«Статья на десять тысяч слов, заново изучающая асинхронное программирование JavaScript»

1. padStart() и padEnd()

Методы padStart() и padEnd() используются для заполнения длины строки. Если строка недостаточно длинная, она будет заполнена в начале или в конце.

(1) padStart()

padStart()Используется для завершения головы. Этот метод имеет два параметра, где первый параметр — это число, указывающее длину строки после завершения, а второй параметр — это строка, используемая для завершения. ​

Возвращает исходную строку, если длина исходной строки равна или превышает указанную минимальную длину:

'x'.padStart(1, 'ab') // 'x'

Если сумма длины строки, используемой для завершения, и исходной строки превышает указанную минимальную длину, завершенная строка, превышающая количество цифр, будет усечена:

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

Если второй параметр опущен, по умолчанию для завершения длины используются пробелы:

'x'.padStart(4, 'ab') // 'a   '

Обычно функция padStart() используется для указания количества цифр для завершения значения. Одно из требований, которое я недавно сделал, состоит в том, чтобы дополнить возвращаемый номер страницы тремя цифрами. Например, первая страница отображается как 001, а это Метод можно использовать для работы:

"1".padStart(3, '0')   // 输出结果: '001'
"15".padStart(3, '0')  // 输出结果: '015'

(2) padEnd()

padEnd()Используется для завершения хвоста. Этот метод также получает два параметра, первый параметр — это максимальная длина действительного завершения строки, а второй параметр — строка, используемая для завершения:

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

2. Object.values() и Object.entries()

Метод Object.keys был представлен в ES5, а Object.values ​​и Object.entries, соответствующие Object.keys, были введены в ES8 в качестве дополнительных средств обхода объекта для цикла for...of. Оба они используются для обхода объекта, который возвращает массив собственных перечисляемых свойств данного объекта (за исключением унаследованных свойств и свойств Symbol), элементы массива располагаются в том же порядке, что и возвращаемые обычным циклом по объекту, возвращаемый значения этих трех элементов следующие:

  • Object.keys(): возвращает массив, содержащий имена ключей объектов;
  • Object.values(): возвращает массив, содержащий значения ключей объекта;
  • Object.entries(): возвращает массив, содержащий имена и значения ключей объектов.
let obj = { 
  id: 1, 
  name: 'hello', 
  age: 18 
};
console.log(Object.keys(obj));   // 输出结果: ['id', 'name', 'age']
console.log(Object.values(obj)); // 输出结果: [1, 'hello', 18]
console.log(Object.entries(obj));   // 输出结果: [['id', 1], ['name', 'hello'], ['age', 18]

Уведомление

  • Все значения в массиве, возвращаемом методом Object.keys(), являются строками, что означает, что значения ключей, не являющиеся строками, будут преобразованы в строки.
  • Значения свойств в результирующем массиве — это сами объектыперечислимое свойство, за исключением унаследованных свойств.

3. Расширение функций

ES2017 указывает, что список параметров функции может заканчиваться запятой:

function person( name, age, sex, ) {}

Основная функция этой функции — облегчить использование git для совместной разработки несколькими людьми, чтобы изменить одну и ту же функцию и уменьшить ненужные изменения строк.

4. Новые возможности ES9 (2018)

1. для ожидания…оф

for await...ofметод называетсяасинхронный итератор, этот метод в основном используется для обхода асинхронных объектов.

for await...ofОператор создает итерационный цикл по асинхронным или синхронным итерируемым объектам, включая String, Array, подобные массиву, Map, Set и настраиваемые асинхронные или синхронные итерируемые объекты. Это утверждение может быть использовано только вasync functionИспользовать внутрь:

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

async function test () {
   let arr = [Gen(2000),Gen(100),Gen(3000)]
   for await (let item of arr) {
      console.log(Date.now(),item)
   }
}
test()

Выходной результат:image.png

2. Promise.prototype.finally()

ES2018 добавляет метод finally() в Promise, который представляет собой метод, который будет выполняться независимо от того, будет ли экземпляр Promise в конечном итоге успешным или неудачным:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const one = '1';
    reject(one);
  }, 1000);
});

promise
  .then(() => console.log('success'))
  .catch(() => console.log('fail'))
  .finally(() => console.log('finally'))

Функция finally() не принимает параметры, а внутренняя часть finally() обычно не знает результата выполнения экземпляра промиса, поэтому обычно то, что выполняется в методе finally(), — это операции, которые не имеют ничего общего с промисом. государство.

3. Оператор разброса объекта

Оператор спреда появился в ES6, но он может действовать только на массивы, а оператор спреда в ES2018 может действовать на объекты: ​

(1) Организация элементов в объекты

const obj = {a: 1, b: 2, c: 3};
const {a, ...rest} = obj;
console.log(rest);    // 输出 {b: 2, c: 3}

(function({a, ...obj}) {
  console.log(obj);    // 输出 {b: 2, c: 3}
}({a: 1, b: 2, c: 3}));

(2) Расширить объект до элемента

const obj = {a: 1, b: 2, c: 3};
const newObj ={...obj, d: 4};
console.log(newObj);  // 输出 {a: 1, b: 2, c: 3, d: 4}

(3) Может использоваться для объединения объектов

const obj1 = {a: 1, b:2};
const obj2 = {c: 3, d:4};
const mergedObj = {...obj1, ...obj2};
console.log(mergedObj);  // 输出 {a: 1, b: 2, c: 3, d: 4}

Пять, новые функции ES10 (2019 г.)

1. ОбрезатьНачало() и ОбрезатьКонец()

До ES10 JavaScript предоставлял метод trim() для удаления пробелов в начале и конце строки. В ES9 предложены методы trimStart() и trimEnd() для удаления пробелов в начале и в конце строки: пробелы, табуляции, символы новой строки и другие пробелы.

(1) TrimStart ()

Поведение метода trimStart() такое же, как иtrim()то же самое, но возвращаетНовая строка с удаленным пробелом в начале исходной строки, который не изменяет исходную строку:

const s = '  abc  ';

s.trimStart()   // "abc  "

(2) обрезатьКонец()

Поведение метода trimEnd() такое же, как иtrim()то же самое, но возвращаетНовая строка с удаленным пробелом в конце исходной строки, который не изменяет исходную строку:

const s = '  abc  ';

s.trimEnd()   // "  abc"

Обратите внимание, что ни один из методов не работает с типами null, undefined и Number.

2. плоская() и плоская карта()

(1) квартира ()

В ES2019 метод flat() используется для создания и возврата нового массива, содержащего те же элементы, что и массив, для которого он вызвал flat(), за исключением того, что любые элементы, которые сами являются массивами, выравниваются и заполняются возвращаемым массивом. множество:

[1, [2, 3]].flat()        // [1, 2, 3]
[1, [2, [3, 4]]].flat()   // [1, 2, [3, 4]]

Когда никакие параметры не переданы, flat() будет выравнивать только один уровень вложенности по умолчанию.Если вы хотите выровнять больше уровней, вам нужно передать числовой параметр в flat(), который указывает количество уровней, которые нужно выровнять:

[1, [2, [3, 4]]].flat(2)  // [1, 2, 3, 4]

Если в массиве есть пустые элементы, он будет пропущен напрямую:

[1, [2, , 3]].flat());    //  [1, 2, 3]

Если переданный параметр меньше или равен 0, будет возвращен исходный массив:

[1, [2, [3, [4, 5]]]].flat(0);    //  [1, [2, [3, [4, 5]]]]
[1, [2, [3, [4, 5]]]].flat(-10);  //  [1, [2, [3, [4, 5]]]]

(2) плоская карта ()

Метод flatMap() сопоставляет каждый элемент с помощью функции сопоставления, а затем сжимает результат в новый массив. Это почти то же самое, что map и flat с глубиной конкатенации, равной 1, но flatMap обычно немного более эффективен при объединении в один метод. Этот метод возвращает новый массив, в котором каждый элемент является результатом функции обратного вызова, а значение глубины структуры равно 1.

[1, 2, 3, 4].flatMap(x => x * 2);      //  [2, 4, 6, 8]
[1, 2, 3, 4].flatMap(x => [x * 2]);    //  [2, 4, 6, 8]

[1, 2, 3, 4].flatMap(x => [[x * 2]]);  //  [[2], [4], [6], [8]]
[1, 2, 3, 4].map(x => [x * 2]);        //  [[2], [4], [6], [8]]

3. Object.fromEntries()

Метод Object.fromEntries() преобразует список пар ключ-значение в объект. Этот метод эквивалентен обратному методу Object.entries(). Метод Object.entries() возвращает массив пар ключ-значение для собственных перечисляемых свойств данного объекта, а метод Object.fromEntries() преобразует список пар ключ-значение в объект.

const object = { key1: 'value1', key2: 'value2' }
 
const array = Object.entries(object)  // [ ["key1", "value1"], ["key2", "value2"] ]
 
 
Object.fromEntries(array)             // { key1: 'value1', key2: 'value2' }

Существуют две основные цели использования этого метода:

(1) Преобразовать массив в объект

const entries = [
  ['foo', 'bar'],
  ['baz', 42]
]
Object.fromEntries(entries)  //  { foo: "bar", baz: 42 }

(2) Преобразование карты в объект

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
])
Object.fromEntries(entries)  //  { foo: "bar", baz: 42 }

4. Описание символа

При создании символа через Symbol() в качестве параметра можно указать строку в качестве описания:

let dog = Symbol("dog");  // dog 为描述 

До ES2019 для получения описания значения Symbol требовался метод String или метод toString:

String(dog);              // "Symbol(dog)" 
dog.toString();           // "Symbol(dog)" 

ES2019 добавляет описание свойства для прямого доступаописывать:

dog.description;  // dog

5. toString()

ES2019 расширяет метод функций toString(), который ранее выводил только код функции, но опускал комментарии и пробелы. Функция toString() в ES2019 сохранит комментарии, пробелы и т. д., то есть на выходе будет исходный код:

function sayHi() {
  /* dog */
  console.log('wangwang');
}

sayHi.toString();  // 将输出和上面一样的原始代码

6. catch

До ES2019 у catch были параметры, но во многих случаях блоки catch были избыточными. И теперь можно без аргументов:

// ES2019 之前
try {
   ...
} catch(error) {
   ...
}

// ES2019 之后
try {
   ...
} catch {
   ...
}

6. Новые функции ES11 (2020 г.)

1. BigInt

В JavaScript числовой тип Number представляет собой 64-битное число с плавающей запятой**, поэтому существуют определенные ограничения на точность вычислений и диапазон представления. ES2020 добавил тип данных BigInt, который является восьмым базовым типом, представленным в JavaScript. BigInt может представлять произвольно большие целые числа. Его синтаксис следующий:

BigInt(value);

где значение — числовое значение созданного объекта. Может быть строкой или целым числом.

В JavaScript наибольшее целое число, которое может точно представлять примитивный тип Number, равно 253. Так что рано будут такие вопросы, как:

let max = Number.MAX_SAFE_INTEGER;    // 最大安全整数

let max1 = max + 1
let max2 = max + 2

max1 === max2   // true

С BigInt этой проблемы больше не существует:

let max = BigInt(Number.MAX_SAFE_INTEGER);

let max1 = max + 1n
let max2 = max + 2n

max1 === max2   // false

Вы можете использовать оператор typeof, чтобы определить, относится ли переменная к типу BigInt (возвращает строку «bigint»):

typeof 1n === 'bigint'; // true 
typeof BigInt('1') === 'bigint'; // true 

также черезObject.prototype.toStringметод, чтобы определить, имеет ли переменная тип BigInt (возвращает строку «[object BigInt]»):

Object.prototype.toString.call(10n) === '[object BigInt]';    // true

Обратите внимание, что BigInt и Number равны не строго, а условно:

10n === 10 // false 
10n == 10  // true 

Число и BigInt можно сравнить:

1n < 2;    // true 
2n > 1;    // true 
2 > 2;     // false 
2n > 2;    // false 
2n >= 2;   // true

2. Нулевой оператор объединения (??)

При написании кода, если свойство не равно null и не определено, то получите свойство, если свойство равно null или undefined, примите значение по умолчанию:

const name = dogName ? dogName : 'default'; 

Это можно упростить с помощью || :

const name =  dogName || 'default'; 

Однако в способе написания || есть определенные недочеты: когда dogName равен 0 или false, он также переходит к логике по умолчанию. Итак, ES2020 представил оператор ??. Возвращает значение справа от ??, только если левая часть ?? равна нулю или не определена:

const dogName = false; 
const name =  dogName ?? 'default';  // name = false;

3. Необязательный оператор цепочки (?.)

Во время разработки нам часто нужно получить глубокие свойства, такие как system.user.addr.province.name. Однако перед получением атрибута name необходимо пошагово судить о том, существует ли предыдущий атрибут, иначе будет сообщено об ошибке:

const name = (system && system.user && system.user.addr && system.user.addr.province && system.user.addr.province.name) || 'default';

Чтобы упростить описанный выше процесс, в ES2020 был представлен «оператор цепочки оценок» ?., необязательный оператор цепочки ( ?. ), который позволяет считывать значения свойств глубоко в цепочке связанных объектов без необходимости явной проверки каждой ссылки в цепочке. это эффективно. Оператор ?. функционирует так же, как оператор .chain, за исключением того, что он не вызывает ошибки, если ссылка имеет значение null или не определено, а выражение short-circuit возвращает значение undefined. При использовании с вызовами функций возвращает неопределенное значение, если данная функция не существует.

const name = system?.user?.addr?.province?.name || 'default';

Необязательный оператор цепочки сделает выражения более короткими и лаконичными при попытке доступа к свойствам объекта, которые могут не существовать. Необязательный оператор цепочки также полезен при изучении содержимого объекта, если вы не уверены, какие свойства должны присутствовать.

Дополнительные цепи бывают трех видов:

a?.[x]
// 等同于
a == null ? undefined : a[x]

a?.b()
// 等同于
a == null ? undefined : a.b()

a?.()
// 等同于
a == null ? undefined : a()

Этот оператор может решить множество проблем при разработке на TypeScript.

7. Новые функции ES12 (2021 г.)

1. String.prototype.replaceAll()

Метод replaceAll() вернет новую строку, и все символы, соответствующие правилам сопоставления, будут заменены.Правила замены могут быть строками или регулярными выражениями.

let string = 'hello world, hello ES12'
string.replace(/hello/g,'hi')    // hi world, hi ES12
string.replaceAll('hello','hi')  // hi world, hi ES12

Обратите внимание, что когда replaceAll использует регулярные выражения, если это не глобальное совпадение (/g), будет выдано исключение:

let string = 'hello world, hello ES12'
string.replaceAll(/hello/,'hi') 
// Uncaught TypeError: String.prototype.replaceAll called with a non-global

2. Разделитель чисел

Разделители чисел могут создавать визуальные разделители между числами, разделять числа символом подчеркивания _, делать числа более читабельными и могут быть размещены в любом месте внутри чисел:

const money = 1_000_000_000
//等价于
const money = 1000000000

Эта новая функция также поддерживает использование в восьмеричных числах:

const number = 0o123_456
//等价于
const number = 0o123456

3. Promise.any

Promise.any — это новая функция в ES2021.Он получает итерируемый объект Promise (например, массив) и, если одно из обещаний выполняется успешно, возвращает успешное обещание. Если ни одно из обещаний в итерируемом объекте не выполнено (т. е. все обещания терпят неудачу/отклоняются), верните невыполненное обещание и экземпляр типа AggregateError , подкласс Error, используемый для группировки одной ошибки.

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.resolve('result'),
]

Promise.any(promises).then((value) => {
  console.log('value: ', value)
}).catch((err) => {
  console.log('err: ', err)
})

// 输出结果:value:  result

Если все входящие промисы терпят неудачу:

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.reject('ERROR C'),
]

Promise.any(promises).then((value) => {
  console.log('value:', value)
}).catch((err) => {
  console.log('err:', err)
  console.log(err.message)
  console.log(err.name)
  console.log(err.errors)
})

Выходной результат:

err:AggregateError: All promises were rejected
All promises were rejected
AggregateError
["ERROR A", "ERROR B", "ERROR C"]