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
скопировать код