JavascriptСкорость обновления слишком высока, чтобы идти в ногу с темпом, и есть несколько вспомогательных руководств.Сегодня я представлю новые функции и возможности ES2016 ~ ES2018 вместе с подробными примерами кода.
Следующее представлено в порядке версий JS:
1. Array.prototype.includes
includesЭто метод экземпляра массива. Функция этого метода очень проста: он используется для определения того, существует ли элемент в массиве.indexOf, разница между нимиindexOfне в состоянии судитьNaN, как показано на рисунке:
const arr = [1, 2, 3, 4, NaN];// es5if (arr.indexOf(3) >= 0) { console.log(true)}// es2016if (arr.includes(1)) { console.log(true)}// 注:indexOf不支持检查`NaN`arr.indexOf(NaN) // -1arr.includes(NaN) // trueскопировать код
2. Оператор возведения в степень
оператор возведения в степень: **, который заменяет предыдущий метод возведения в степеньMath.pow,
Способ применения следующий:
// 之前Math.pow(3, 2) // 9// 现在3**2 // 9скопировать код
1. Object.values()
Object.valuesМетоды иObject.keysТочно так же возвращаемый тип — это массив, а возвращаемое значение — это набор значений объекта.Следует отметить, что оба метода возвращают свои собственные свойства, исключая любые свойства в цепочке прототипов, как показано на рисунке:
const cars = { BMW: 3, Tesla: 2, Toyota: 1}// es5const vals = Object .keys(cars) .map(key => cars[key]) console.log(vals) // [3, 2, 1]// es2016const values = Object.values(cars)console.log(values) // [3, 2, 1]скопировать код
2. Object.entries()
Object.entries()метод немного похожObject.keysа такжеObject.valuesКомбинация , возвращаемый тип представляет собой массив, и каждый элемент массива также является массивом, включая два элемента: ключ и значение.Преимущество этого метода в том, что вы можете передатьfor ofПройдите ключ/значение один раз; возвращаемое значение (объект) Object.entries() также может быть напрямую преобразовано вMap:
пример 1, пересекая:
const cars = { BMW: 3, Tesla: 2, Toyota: 1}// es5的遍历方式// 需要把`key`取出来,再遍历Object .keys(cars) .forEach(key => { console.log(`key: ${key}, value: ${cars[key]}`) })// es2017// Object.entries(carts):// [// ['BMW', 3],// ['Tesla', 2],// ['Toyota', 1]// ]for (let [key, value] of Object.entries(cars)) { console.log(`key: ${key}, value: ${cars[key]}`)}скопировать код
Пример 2, преобразовать объект непосредственно вMap:
const cars = { BMW: 3, Tesla: 2, Toyota: 1}// es5const map1 = new Map()Object.keys(cars).map(key => { map1.set(key, cars[key])})console.log(map1) // Map { 'BMW': 3, 'Tesla': 2, 'Toyota': 1 }// es2016const map2 = new Map(Object.entries(cars))console.log(map2) // Map { 'BMW': 3, 'Tesla': 2, 'Toyota': 1 }скопировать код
3. String padding
String добавляет два метода экземпляра —padStartа такжеpadEnd, эти два метода могут добавлять другие строки в начало/конец строки:
// 'someStr'.padStart(字符数, [,添加的字符])'hello'.padStart('10', 'a') // 'aaaaahello', 添加了5个字符`a`后一共`10`个字符'hello'.padEnd('10', 'b') // 'hellobbbbb''hello'.padStart('7') // ' hello', 在头部添加两个个空格скопировать код
3.1 Пример стартовой площадки
const formatted = [ 0, 1, 12, 123, 1234, 12345 ] .map(num => num.toString().padStart(10, '0') )console.log(formatted)// 输出:// [// '0000000000',// '0000000001',// '0000000012,'// '0000000234,'// '0000001234,'// '0009012345'// ]скопировать код
3.2 пример padEnd
const cars = { '🚙BMW': '10', '🚘Tesla': '5', '🚖Lamborghini': '0'}Object .entries(cars) .map(([name, count]) => { console.log(`${name.padEnd(20, ' -')} Count: ${count.padStart(3, '0')}`) });//输出:// 🚙BMW - - - - - - - Count: 010// 🚘Tesla - - - - - - Count: 005// 🚖Lamborghini - - - Count: 000скопировать код
4.Object.getOwnPropertyDescriptors
Эффект этого метода заключается в дополненииObject.assignфункция, основанная на объекте мелкого клонирования, также скопируетgetterа такжеsetterметод:
В следующем примере используетсяObject.definePropertiesскопировать исходный объектCarна новый объектElectricCarпоказыватьObject.assignа такжеObject.getOwnPropertyDescriptorsс разница.
const Car = { name: 'BMW', price: 100000, set discount(x) { this.d = x }, get discount() { return this.d }}console.log(Object.getOwnPropertyDescriptor(Car, 'discount')// 输出:// {// get: [Function: get],// set: [Function: set],// enumerable: true,// configurable: true// }const ElectricCar = Object.assign({}, Car)console.log(Object.getOwnPropertyDescriptor(ElectricCar, 'discount'))// 输出:// {// value: undefined,// writable: true,// enumerable: true,// configurable: true// }// 使用`Object.assign`创建`ElectricCar`后,属性`getter`和`setter`丢失了скопировать код
использоватьObject.getOwnPropertyDescriptorsназад:
const Car = { name: 'BMW', price: 100000, set discount(x) { this.d = x }, get discount() { return this.d }}const ElectricCar2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(Car))// 输出:// {// get: [Function: get], <-----👈// set: [Function: set], <-----👈// enumerable: true,// configurable: true // }скопировать код
5. Добавьте запятую в конце последнего параметра функции
Это небольшая функциональная точка, добавление запятой в конце последнего параметра параметра функции может избежатьgit blameЗапрашивать изменения, которых не было у предыдущего автора, пример кода:
// 假设这个函数由 `程序员_1` 创建// 这个函数最后一个参数`age`后没有逗号function Person( name, age) { this.name = name this.age = age}// 如果 `程序员_2` 这时有了以下修改function Person( name, age, /* 那么这个`,`逗号也会引起`git blame`认为 `程序员_1` 修改了这一行*/ gender /* 添加了新参数 */) { // 新添加 this.name = name this.age = age this.gender = gender // 新添加}// es2017对这个混淆的处理办法是:// 通过 `程序员_1`在`age`末尾添加`,`逗号// 更新如下:// 假设这个函数由 `程序员_1` 创建// 在最后一个参数`age`后添加`,`逗号function Person( name, age, /* 添加逗号 */) { this.name = name this.age = age}скопировать код
6. Async/Await
Эта функция, безусловно, самая важная функция,asyncФункция позволяет нам избежать частых вызововотвратительный callback, чтобы код оставался чистым и аккуратным.
когда компилятор входитasyncПосле функции встречаawaitКлючевое слово приостановит выполнение, вы можете поставитьawaitпоствыражение какpromise, пока не будет обещаноresolveилиrejectПосле этого функция возобновит выполнение,
В частности, посмотрите на следующий код:
// es5的`Promise`function getAmount(userId) { getUser(userId) .then(getBankBalance) .then(amount => { console.log(amount) })}// es2017的`async`async function getAmount2(userId) { var user = await getUser(userId) var amount = await getBankBalance() console.log(amount)}getAmount('1') // $1,000getAmount2('1') // $1,000function getUser(userId) { return new Promise(resolve => { setTimeout(() => { resolve('张三') }, 1000) })}function getBankBalance() { return new Promise((resolve, reject) => { setTimeout(() => { if (user === '张三') { resolve('$1,000') } else { resolve('Unknown User') } }, 1000) })}скопировать код
6.1 Сама функция Async возвращает обещание
Поскольку асинхронная функция возвращает обещание, чтобы получить возвращаемое значение асинхронной функции, вам нужно выполнить возвращенное обещание.thenоценивать.
В частности, посмотрите на следующий код:
async function doubleAndAdd(a, b) { a = await doubleAfter1Sec(a) b = await doubleAfter1Sec(b) return a + b}doubleAndAdd(1, 2).then(console.log) // 5 async function doubleAfter1Sec(param) { return new Promise(resolve => { setTimeout(() => { resolve(param * 2) }, 1000) })}скопировать код
6.2 Параллельный вызов async/await
предыдущая функцияdoubleAndAddВызывали двоих по очередиasyncфункция, но приходится ждать 1 секунду для каждого вызова, низкая производительность; потому что параметрaи параметрыbМежду ними нет связи, поэтому мы можем использоватьPromise.allчтобы выполнить эти два вызова параллельно:
async function doubleAndAdd(a, b) { // 使用`Promise.all` // 这个地方使用数组`解构` // 来得到两次调用的结果 const [a, b] = Promise.all([ doubleAfter1Sec(a), doubleAfter1Sec(b) ]) return a + b}doubleAndAdd(1, 2).then(console.log) // 5 async function doubleAfter1Sec(param) { return new Promise(resolve => { setTimeout(() => { resolve(param * 2) }, 1000) })}скопировать код
6.3 Обработка ошибок async/await
async/awaitСуществует множество способов обработки ошибок:
1. Используйте try/catch внутри функции
async function doubleAndAdd(a, b) { try { a = await doubleAfter1Sec(a) b = await doubleAfter1Sec(b) } catch (e) { return NaN } return a + b}doubleAndAdd('one', 2).then(console.log) // NaNdoubleAndAdd(1, 2).then(console.log) // 5 async function doubleAfter1Sec(param) { return new Promise(resolve => { setTimeout(() => { const val = param * 2 isNaN(val) ? reject(NaN) : resolve(val) }, 1000) })}скопировать код
2. Уловить всеawaitвыражение
потому чтоawaitвыражение возвращаетpromise, так что мы можемawaitВыполнять сразу после выраженияcatchобрабатывать ошибки
async function doubleAndAdd(a, b) { a = await doubleAfter1Sec(a).catch(e => console.log(`'a' is NaN`) b = await doubleAfter1Sec(b).catch(e => console.log(`'b' is NaN`)) if (!a || !b) return NaN return a + b}doubleAndAdd('one', 2).then(console.log) // NaN, "a" is NaNdoubleAndAdd(1, 2).then(console.log) // 5 async function doubleAfter1Sec(param) { return new Promise(resolve => { setTimeout(() => { const val = param * 2 isNaN(val) ? reject(NaN) : resolve(val) }, 1000) })}скопировать код
3. поймать всю функцию асинхронного ожидания
async function doubleAndAdd(a, b) { a = await doubleAfter1Sec(a) b = await doubleAfter1Sec(b) return a + b}doubleAndAdd('one', 2) .then(console.log) .catch(console.log) // 使用catchскопировать код
ECMAScript в настоящее время находится на стадии финального проекта и будет официально запущен в июне или июле 2018 года. Все функции, описанные ниже, принадлежатstage-4, который вскоре станет частью ECMAScript 2018.
1. Общая память и атомарность
Это расширенная функция JS и основное улучшение движка JS.
Основная идея разделяемой памяти заключается в том, чтобы привнести многопоточность в JS, чтобы улучшить производительность и высокий параллелизм кода, предыдущий движок JS управляет памятью для управления самой памятью.
Эта возможность реализована новым глобальным объектом SharedArrayBuffer, который находится в блокеразделяемая область памятиДля хранения данных основной поток JS иweb-workerПотоки разделяют эту часть данных.
В настоящее время, если мы хотим обмениваться данными между основным потоком JS и потоком веб-воркеров, мы должны использоватьpostMessageПередача данных между разными потоками сSharedArrayBufferПозже различные потоки могут напрямую обращаться к этому объекту для обмена данными.
Однако общая память между несколькими потоками вызовет состояние гонки.Чтобы избежать этой ситуации, JS вводит原子性глобальный объект. Этот объект предоставляет различные методы для обеспечения того, чтобы память, к которой обращается поток, была заблокирована для безопасности памяти.
2. Ограничение Tagged Template literal (tagged template literal?) снято
Сначала разберитесь с концепцией: что такое литерал тегированного шаблона?
tagged template literalПоявившийся в es2015 и более поздних версиях, позволяет разработчикам настраивать значение встроенной строки. Например, стандартный способ вставить значение в строку:
const userName = '张三'const greetings = `hello ${userName}!`console.log(greetings) // "hello 张三!"скопировать код
существуетtagged template literal, Вы можете использовать функцию для получения жесткокодируемых частей строки через параметры, такие как: [«Hello», '!'] и переменная ['Zhang San'], которая позже заменяется значением, и, наконец, верните любое значение через функцию. Вы хотите результат, функция называетсяTaggedфункция, нижеTaggedфункцияgreetЧтобы расширить приветствия в приведенном выше примере:
const userName = '张三'const greetings = `hello ${userName}!`console.log(greetings) // "hello 张三!早上好!"// hardCodedPartsArray: 字符串写死的各部分, [ "hello ", "!" ]// replacementPartsArray: 字符串里嵌入的变量, [ "张三" ]function greet(hardCodedPartsArray, ...replacementPartsArray) { let str = '' hardCodedPartsArray.forEach((part, i) => { if (i < replacementPartsArray.length) { str += `${part}${replacementPartsArray[i] || ''}` } else { str += `${part} ${timeGreet()}` // 在结尾添加问候语 } }) return str}function timeGreet() { const hr = new Date().getHours() return hr < 12 ? '早上好!' : hr < 18 ? '下午好!' : '晚上好!'}скопировать код
3. В регулярных выражениях.соответствовать всем символам
Хотя в текущих регулярных выражениях.считается, что точка представляет все символы, на самом деле она не будет соответствовать чему-то вроде\n,\rа также\fДождитесь новой строки.
Например:
// 之前/first.second/.test('first\nsecond'); // falseскопировать код
Это улучшение делает.Оператор точки соответствует любому одиночному символу. Для того, чтобы следующий код работал корректно в любой версии JS, добавим в конце/sмодификатор
//ECMAScript 2018/first.second/s.test('first\nsecond'); // true 注意: /s 👈🏼скопировать код
4. Захват регулярного выражения Именованная группа
Это улучшение приносит полезные функции регуляризации, которые уже поддерживаются в других языках, таких как Java, Python и т. д. Эта функция позволяет разработчикам записывать формат для разных групп в регулярном выражении как(<?name>)Идентификатор имени , а затем соответствующее значение может быть получено группой, идентифицированной по имени в результате сопоставления.
4.1 Базовый пример
// 之前const re1 = /(\d{4})-(\d{2})-(\d{2})/const result1 = re1.exec('2015-01-08')console.log(result1)// 输出:// [// "2015-01-08",// "2015",// "01",// "08",// index: 0,// input: "2015-01-08",// groups: undefined// ]// 现在 (es2018)const re2 = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/uconst result2 = re2.exec('2015-01-08')console.log(result2)// 输出:// [// "2015-01-08",// "2015",// "01",// "08",// groups: {// day: "08",// month: "01",// year: "2015"// },// index: 0,// input: "2015-01-08",// ]скопировать код
4.2 Использование именованных групп в самом регулярном выражении
мы можем использовать формат\k<group name>перед самим регулярным, чтобы обратиться к предыдущей группе. Следующий пример использования показывает:
// 在这个例子里,我们有一个命名组`fruit`,// 它可以匹配`apple`或者`orange`,我们可以用`\k<group name>`(\k<fruit>)// 来引用之前匹配的这个组// 所以等号两边的值是相等的const sameWords = /(?<fruit>apple|orange)==\k<fruit>/usameWords.test('apple==apple') // truesameWords.test('orange==orange') // truesameWords.test('apple==orange') // falseскопировать код
4.3 Использование именованных групп в String.prototype.replace
Функция именованной группы была добавлена вreplaceметод, поэтому мы можем легко заменить строки.
Пример: изменить «имя, фамилия» на «фамилия, имя»:
const re = /(?<firstName>[A-Za-z]+) (?<lastName>[A-Za-z]+)/u'John Lennon'.replace(re, '$<lastName>, $<firstName>') // Lennon Johnскопировать код
5. Остальные свойства объектов
Оператор отдыха...(три точки) позволяет убрать остальные свойства объекта
5.1 Используйте свойства Rest, чтобы получить свойства, которые вы хотите использовать
let { name, age, ...remaining} = { name: '张三', age: 20, gender: '男', address: 'xxxxx'}name // '张三'age // 20скопировать код
5.2 Вы даже можете удалить ненужные свойства
// 如果我们想要删除address属性,// 但是我们又不想要遍历对象重新创建新对象// 我们只要简单的解构出这个要移除的属性// 剩下的没有解构的对象// 就是我们想要留下的对象let {address, ...cleanObj} = { name: 'john', address: '北京市海淀区', gender: '男'}cleanObj // {name, gender}скопировать код
6. Распространяйте свойства объектов
Свойство Spread похоже на свойство Rest, и это тоже три точки....оператор, за исключением того, что Spread используется для создания новых объектов.
const person = {name: 'john', age: 20}const address = {city: 'Beijing', country: 'china'}const personWithAddress = { ...person, ...address}personWithAddress // {name, age, city, country}скопировать код
7. Регулярное утверждение просмотра назад
Это улучшение регуляризации позволяет нам гарантировать, что некоторые строки существуют до некоторых строк.
можно использовать набор(?<=...)(вопросительный знак, меньше или равно) Найдите следующее положительное утверждение.
Делая шаг вперед, вы можете использовать(?<!...(знак вопроса, меньше восклицательного знака) Найдите последующее отрицательное утверждение.
Положительное утверждение: например, мы хотим быть уверены, что символ#появляется в словеwinningраньше, т.е.#winning, только возвращаетwinning:
/(?<=#).*/.test('winning') // false/(?<=#).*/.test('#winning') // true// 之前'#winning'.match(/#.*/)[0] // '#winning'// es2018'#winning'.match(/(?<=#).*/)[0] // 'winning', 没有 #, #只是为了验证скопировать код
Отрицательное утверждение: например, мы хотим убрать число со знаком # вместо $
'this is a test signal $1.23'.match(/(?<!\$)\d+\.\d+/) // null'this is a test signal #2.43'.match(/(?<!\$)\d+\.\d+/)[0] // 2.43скопировать код
8. Обычный escape-символ свойства Unicode
Сопоставить все символы Юникода с регулярным выражением сложно. картина\w,\W,\dи т. д. могут соответствовать только английским символам и цифрам, но что нам делать с числами, которые появляются на других языках, таких как греческий?
Экранирующий символ свойства Unicode предназначен для решения этой проблемы. Это заставляет Unicode добавлять описание к каждому символу.metadata.
Пример: база данных Unicode группирует все символы хинди в одно значение.DevanagariсвойстваScriptи другое значение также дляDevanagariсвойстваScript_Extensionsпод группой, чтобы мы могли искатьScript_Extensionsчтобы получить все символы хинди.
Starting in ECMAScript 2018, we can use \p to escape characters along with {Script=Devanagari} to match all those Indian characters. That is, we can use: \p{Script=Devanagari} in the RegEx to match all Devanagari characters.
Начиная с ES2018 мы можем использовать\pСопоставьте все символы хинди с escape-символом {Script=Devanagari}, то есть используйте escape-символ\p{Script=Devanagari}чтобы соответствовать всем символам Деванагари.
// 下面的正在匹配多个北印度语字符// ps: 这里一共有三个北印度语字符/^p{Script=Devanagari}+$/u.test('हिन्दी') // trueскопировать код
Точно так же Unicode присваивает атрибуту все греческие символы.Script_Extensions(а такжеScript) значениеGreekсгруппировать, чтобы мы могли использоватьScript_Extensions=GreekилиScript=Greekдля поиска всех греческих символов.
То есть мы можем использовать escape-символы\p{Script=Greek}чтобы соответствовать всем греческим символам:
/\p{Script_Extensions=Greek}/u.test('π') // trueскопировать код
Кроме того, в базе данных Unicode хранится множество типов символов Emoji с логическими свойствами.Emoji,Emoji_Component,Emoji_Presentation,Emoji_Modifierа такжеEmoji_Modifier_Base, значениеtrueЧтобы сгруппировать по, мы можем сделать это, используяEmojiДля поиска всех символов эмоджи.
То есть, экранируя символ\p{Emoji}чтобы соответствовать различным символам Emoji
/\p{Emoji}/u.test('❤️')// 下面的例子匹配失败,因为黄色的emoji字符不需要`Emoji_Modifier`/\p{Emoji}\p{Emoji_Modifier}/u.test('✌️'); //false// 下面的匹配一个emoji字符,`\p{Emoji}`跟着一个`\p{Emoji_Modifier}`/\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //true// 解释:// 默认情况下`胜利`的emoji字符是黄色的,// 如果我们使用棕色、黑色或者其他颜色的变种emoji,// 它们被当做为原始Emoji字符的变种,使用两个unicode字符来表示,// 一个代表原始的emoji字符,跟着的一个unicode字符表示颜色//// 所以在下面的例子里,即使我们只看到了一个棕色的胜利emoji图标,// 但是它实际上使用了两个unicode字符,一个是emoji,另一个是棕色。//// 在Unicode数据库里,这些颜色有`Emoji_Modifier`属性。// 所以我们需要使用`\p{Emoji}`和`\p{Emoji_Modifier}`// 来完整的匹配棕色emoji/\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //trueскопировать код
Наконец, мы можем использовать escape-символ заглавной буквы «P» (\P) вместо маленькой p (\p), чтобы отменить совпадения.
Наконец, мы можем использовать заглавную P (\P) escape-символ для соответствия и нижний регистр\pСоответствует противоположному содержанию.
8. Promise.prototype.finally()
finally()— это метод экземпляра, недавно добавленный в Promise. Основное использование вresolveилиrejectПосле выполнения функции обратного вызова выполняется задача очистки.
finallyФункция обратного вызова не принимает никаких параметров и будет выполняться независимо от обстоятельств.
// Resolve示例let started = truelet myPromise = new Promise((resolve, reject) => { resolve('all good')}).then(val => { console.log(val) // 'all good'}).catch(e => { console.log(e) // 跳过}).finally(() => { console.log('这个函数总是会被执行') started = false // 清理})// Reject示例let started = truelet myPromise = new Promise((resolve, reject) => { reject('reject apple')}).then(val => { console.log(val) // 'reject apple'}).catch(e => { console.log(e) // 跳过}).finally(() => { console.log('这个函数总是会被执行') started = false // 清理})// 错误case 1// 从Promise抛出错误let started = truelet myPromise = new Promise((resolve, reject) => { throw new Error('error')}).then(val => { console.log(val) // 跳过}).catch(e => { console.log(e) // 因为有error,所以catch被调用}).finally(() => { console.log('这个函数总是会被执行') started = false // 清理})// 错误case 2// 从`catch` case 抛出错误let started = truelet myPromise = new Promise((resolve, reject) => { throw new Error('something happened')}).then(val => { console.log(val) // 跳过}).catch(e => { throw new Error('throw another error')}).finally(() => { console.log('这个函数总是会被执行') started = false // 清理 // 注意,从*catch*里抛出的错误需要在其他地方处理})скопировать код
9. Асинхронный цикл
Этооченьполезная функция. По сути, это позволяет нам легко создавать циклы в асинхронных функциях.
Эта функция добавляет новыйfor-await-ofЦиклы позволяют нам вызывать асинхронную функцию, которая возвращает промис (или массив промисов) в цикле.
Круто то, что этот цикл ждет каждого промисаresolveЗатем переходите к следующему циклу.
const promises = [ new Promise(resolve => resolve(1)), new Promise(resolve => resolve(2)), new Promise(resolve => resolve(3)),]// 之前// for-of使用正常的同步循环// 不会等待promise resolveasync function test1() { for (const obj of promises) { console.log(obj) // 输出3个promise对象 }}// 之后// for-await-of 使用Async循环// 为每个循环等待promise resolveasync function test2() { for await (const obj of promises) { console.log(obj) // 输出1, 2, 3 }}test1() // promise, promise, promisetest2() // 1, 2, 3скопировать код