Поскольку все примечания имеют значение слова, близко к 4 Вт, они разделены на верхнюю и нижнюю части. Содержание второй части планируется обновлять в ближайшие два дня.
Если вы считаете, что это хорошо написано, пожалуйста, дайтеstar, если вы хотите прочитать все примечания в верхней и нижней частях, нажмитеЧитать статью полностью
Прочтите книгу «Углубленное понимание ES6» и систематизируйте заметки (часть 1)
Прочтите книгу «Глубокое понимание ES6» и систематизируйте заметки (ниже)
предисловие
существуетECMAScript6До окончательной доработки стандарта были проведены некоторые экспериментальные转译器(Transpiler), такие как GoogleTraceur, вы можете изменить код сECMAScript6Перевести вECMAScript5. Но большинство из них имеют очень ограниченную функциональность или их трудно подключить к существующимJavaScriptПостройте трубопровод.
Однако впоследствии появились новые транспиляторы.6to5изменил все это. Его легко установить, он хорошо интегрируется с существующими инструментами и генерирует читаемый код, поэтому постепенно распространяется как лесной пожар.6to5который сейчас известенBabel.
Эволюция ECMAScript6
JavaScript核心的语言特性是在标准ECMA-262中被定义,该标准中定义的语言被称作ECMAScript,它是JavaScript的子集。
Дорога эволюции:
-
停滞不前: постепенно появляетсяAjaxсоздал динамичныйWebНовая эра цитат, а с 1999 г. 3-е издание.ECMA-262С момента своего выпуска,JavaScriptНо ничего не изменилось. -
转折点: 2007 г.,TC-39Комитет объединил большое количество проектов спецификаций вECMAScript4Среди них очень обширны новые возможности языка, в том числе: модули, классы, наследование классов, частные члены объекта и многие другие функции. -
分歧:ОднакоTC-39внутри организацииECMAScript4Проект вызвал огромные различия, некоторые участники считают, что не следует добавлять слишком много новых функций в четвертую редакцию стандарта за один раз, и технические руководители из Yahoo, Google и Microsoft совместно представилиECMAScript3.1проект как следующее поколениеECMAScript, который является лишь небольшой дополнительной модификацией существующего стандарта. Поведение больше ориентировано на оптимизацию функций атрибутов и поддержку нативныхJSONА также добавление новых методов к существующим объектам. -
从未面世的ECMAScript4: 2008 год,JavaScriptосновательBrendan EichобъявитьTC-39С одной стороны, Комитет принесет разумный прогрессECMAScript3.1работа по стандартизации, с другой стороны, временноECMAScript4Большинство изменений синтаксиса и функций, предложенных в стандарте, отложены. -
ECMAScript5: стандартизированныйECMAScript3.1в конце концов какECMA-262Пятое издание было официально выпущено в 2009 году и также называлосьECMAScript5. -
ECMAScript6:существуетECMAScript5После публикации,TC-39Комиссия заморожена в 2013 годуECMAScript6черновик, новые функции не добавляются. 2013 годECMAScript6Проект был выпущен после 12 месяцев обсуждений и отзывов всех сторон. 2015ECMAScript6официально выпущен и названECMAScript 2015.
привязка на уровне блока
过去JavaScript中的变量声明机制一直令我们感到困惑,大多数类C语言在声明变量的同时也会创建变量,而在以前的JavaScript中,何时创建变量要看如何声明的变量,ES6引入块级作用域可以让我们更好的控制作用域。
объявление var и механизм подъема переменных
В: Подъемный механизм (hoisting) это что?
Ответ: в области функции или глобальной области по ключевому словуvarОбъявленные переменные, независимо от того, где они фактически объявлены, обрабатываются как переменные, объявленные в верхней части текущей области видимости, что мы часто называем механизмом подъема.
Следующий пример кода иллюстрирует этот механизм продвижения:
function getValue (condition) {
if (condition) {
var value = 'value'
return value
} else {
// 这里可以访问到value,只不过值为undefined
console.log(value)
return null
}
}
getValue(false) // 输出undefined
Вы можете видеть в приведенном выше коде, что когда мы передаемfalseзначение, но по-прежнему имеют доступ кvalueЭта переменная, потому что на этапе прекомпиляцииJavaScriptДвижок изменит код вышеуказанной функции в следующую форму:
function getValue (condition) {
var value
if (condition) {
value = 'value'
return value
} else {
console.log(value)
return null
}
}
После приведенного выше примера мы можем найти: переменнаяvalueОбъявление поднимается наверх области видимости функции, а инициализация остается на месте, потому чтоvalueПеременная объявлена только без присваивания, поэтому приведенный выше код распечатаетundefined.
объявление блока
Объявления блочного уровня используются для объявления переменных, недоступных за пределами указанной области видимости.Области действия блочного уровня существуют: внутри функций и в блоках.
letутверждение:
-
letзаявление иvarИспользование объявлений в основном такое же. -
letОбъявленные переменные не поднимаются. -
letСуществующая переменная не может быть объявлена повторно в одной и той же области, и будет сообщено об ошибке. -
letОбласть видимости объявленной переменной существует только в текущем блоке, создается при входе программы в блок и уничтожается при выходе программы из блока.
согласно сletОбъявленное правило после изменения приведенного выше кода выглядит следующим образом:
function getValue (condition) {
if (condition) {
// 变量value只存在于这个块中。
let value = 'value'
return value
} else {
// 访问不到value变量
console.log(value)
return null
}
}
constутверждение:constзаявление иletУтверждение в основном такое же, единственное существенное отличие состоит в том,constОн используется для объявления константы, а значение переменной после объявления не может быть изменено, т.е. означает:constОбъявления должны быть инициализированы.
const MAX_ITEMS = 30
// 报错
MAX_ITEMS = 50
我们说的const变量值不可变,需要分两种类型来说:
值类型:变量的值不能改变。
引用类型:变量的地址不能改变,值可以改变。
const num = 23
const arr = [1, 2, 3, 4]
const obj = {
name: 'why',
age: 23
}
// 报错
num = 25
// 不报错
arr[0] = 11
obj.age = 32
console.log(arr) // [11, 2, 3, 4]
console.log(obj) // { name: 'why', age: 32 }
// 报错
arr = [4, 3, 2, 1]
Временная мертвая зона
потому чтоletа такжеconstОбъявленные переменные не подвергаются подъему объявления, поэтому вletа такжеconstЛюбой доступ до объявления переменной (дажеtypeofНи то, ни другое) операции с этой переменной вызывают ошибку:
if (condition) {
// 报错
console.log(typeof value)
let value = 'value'
}
В: Почему я получаю сообщение об ошибке?
отвечать:JavaScriptКогда движок сканирует код на наличие объявлений переменных, он либо поднимает их наверх области видимости (varдекларация) или поместите декларацию вTDZ(временная мертвая зона) в (letа такжеconstутверждение). доступTDZПеременная вTDZ, и тогда к нему можно получить доступ в обычном режиме.
привязка области глобального блока
Все мы знаем: если мы перейдем в глобальную область видимостиvarОбъявите переменную, тогда эта переменная будет смонтирована в глобальный объектwindowначальство:
var name = 'why'
console.log(window.name) // why
но если мы используемletилиconstСоздайте новую переменную в глобальной области видимости, эта переменная не будет добавлена вwindowначальство.
const name = 'why'
console.log('name' in window) // false
Эволюция лучших практик привязки на уровне блоков
существуетES6Ранее было широко распространено мнение, что значение по умолчанию должно бытьletзаменитьvar, потому что для разработчиковletна самом деле с тем, что они хотятvarОпять же, прямая замена логична. Но с развитием времени все более популярным становится другой подход: использование по умолчаниюconst, он будет использоваться только тогда, когда будет определено, что значение переменной необходимо будет изменить позже.letобъявление, потому что большинство переменных не должны изменяться после инициализации, а непредвиденных изменений значений переменных может быть много.bugисточник.
нить
В этой главе оunicodeа обычная часть не сортируется.
литерал модуля
Литералы шаблонов являются расширениямиECMAScriptСинтаксический сахар для базовой грамматики, который предоставляет набор методов для создания, запроса и управления контентом из других языков.DSL, и освобождается отXSSинъекционные атаки иSQLвводить и так далее.
существуетES6До,JavaScriptМногие функции отсутствовали все время:
- многострочная строка: формальная концепция многострочных строк.
- базовое форматирование строк: Возможность внедрить значение переменной в строку.
-
Экранирование HTML:К
HTMLВозможность интерполировать безопасно преобразованные строки.
пока вECMAScript 6В вышеперечисленных задачах используются шаблонные литералы, использование одного из простейших шаблонных литералов выглядит следующим образом:
const message = `hello,world!`
console.log(message) // hello,world!
console.log(typeof message) // string
Следует отметить, что если нам нужно использовать обратные кавычки в строках, нам нужно использовать\бежать следующим образом:
const message = `\`hello\`,world!`
console.log(message) // `hello`,world!
многострочная строка
посколькуJavaScriptС момента своего создания разработчики пытались создавать многострочные строки, следующие:ES6Предыдущий метод:
Добавить в начало новой строки строки\Вы можете выполнить предыдущую строку кода, вы можете использовать этот небольшойbugдля создания многострочных строк.
const message = 'hello\
,world!'
console.log(message) // hello,world
существуетES6После этого мы можем использовать литералы шаблонов для создания многострочных строк путем их непосредственного переноса следующим образом:
在模板字面量中,即反撇号中所有空白字符都属于字符串的一部分。
const message = `hello
,world!`
console.log(message) // hello
// ,world!
заполнитель строки
Самая большая разница между литералами шаблона и обычными строками заключается в функции-заполнителе в строках шаблона, где содержимое заполнителя может быть любым допустимым.JavaScriptВыражения, такие как переменные, выражения, вызовы функций или даже другой литерал шаблона.
const age = 23
const name = 'why'
const message = `Hello ${name}, you are ${age} years old!`
console.log(message) // Hello why, you are 23 years old!
Вложенность шаблонных литералов:
const name = 'why'
const message = `Hello, ${`my name is ${name}`}.`
console.log(message) // Hello, my name is why.
Шаблон этикетки
Теги относятся к строкам, отмеченным перед первой обратной кавычкой литерала шаблона.Каждый тег шаблона может выполнять преобразование литерала шаблона и возвращать конечное строковое значение.
// tag就是`Hello world!`模板字面量的标签模板
const message = tag`Hello world!`
Метка может быть функцией, и функции меток обычно используют функцию неопределенного параметра для определения заполнителей для упрощения процесса обработки данных, например:
function tag(literals, ...substitutions) {
// 返回一个字符串
}
const name = 'why'
const age = 23
const message = tag`${name} is ${age} years old!`
вliteralsпредставляет собой массив, содержащий:
- Пустая строка перед первым заполнителем: "".
- Строка между первым и вторым заполнителем: "is".
- Строка после второго заполнителя: "лет!"
substitutionsТакже массив:
- Первый элемент массива:
nameзначение, то есть:why. - Второй элемент массива:
ageзначение, то есть:23.
Из приведенных выше правил мы можем найти, что:
-
literals[0]Всегда представляет начало строки. -
literalsобщий коэффициентsubstitutionsеще один.
С помощью приведенной выше модели мы можемliteralsа такжеsubstitutionsДва массива чередуются, образуя строку, имитирующую стандартное поведение литералов шаблона, например:
function tag(literals, ...substitutions) {
let result = ''
for (let i = 0; i< substitutions.length; i++) {
result += literals[i]
result += substitutions[i]
}
// 合并最后一个
result += literals[literals.length - 1]
return result
}
const name = 'why'
const age = 23
const message = tag`${name} is ${age} years old!`
console.log(message) // why is 23 years old!
необработанная информация о строке
Доступ к необработанной строке перед преобразованием escape-последовательности в эквивалентную строку можно получить через тег шаблона.
const message1 = `Hello\nworld`
const message2 = String.raw`Hello\nworld`
console.log(message1) // Hello
// world
console.log(message2) // Hello\nworld
функция
значение параметра по умолчанию
существуетES6Раньше вы могли создать функцию и предоставить значения по умолчанию для параметров в этом шаблоне:
function makeRequest (url, timeout, callback) {
timeout = timeout || 2000
callback = callback || function () {}
}
Анализ кода: в приведенном выше примереtimeoutа такжеcallbackявляется необязательным параметром, и если он не передан, ему будет присвоено значение по умолчанию с использованием логического оператора ИЛИ. Однако у этого подхода есть и определенные недостатки, если мы хотим датьtimeoutПередайте значение как0, хотя это значение допустимо, но из-за наличия логического оператора ИЛИ оно в конечном счетеtimeoutназначать2000.
Ввиду описанной выше ситуации мы должны принять более безопасный подход (используяtypeof), чтобы переписать приведенный выше пример:
function makeRequest (url, timeout, callback) {
timeout = typeof timeout !== 'undefined' ? timeout : 2000
callback = typeof callback !== 'undefined' ? callback : function () {}
}
Анализ кода: хотя описанный выше метод безопаснее, нам все равно нужно написать больше кода для реализации этой очень простой операции. Для вышеуказанных проблем,ES6Упрощает процесс предоставления значений по умолчанию для формальных параметров, например:
对于默认参数而言,除非不传或者主动传递undefined才会使用参数默认值(如果传递null,这是一个合法的参数,不会使用默认值)。
function makeRequest (url, timeout = 2000, callback = function () {}) {
// todo
}
// 同时使用timeout和callback默认值
makeRequest('https://www.taobao.com')
// 使用callback默认值
makeRequest('https://www.taobao.com', 500)
// 不使用默认值
makeRequest('https://www.taobao.com', 500, function (res) => {
console.log(res)
})
Влияние значений формальных параметров по умолчанию на объект arguments
существуетES5В нестрогом режиме, если вы модифицируете значения параметров, значения этих параметров будут синхронно отражаться вargumentsобъекта следующим образом:
function mixArgs(first, second) {
console.log(arguments[0]) // A
console.log(arguments[1]) // B
first = 'a'
second = 'b'
console.log(arguments[0]) // a
console.log(arguments[1]) // b
}
mixArgs('A', 'B')
пока вES5В строгом режиме изменение значения параметра больше не отражаетсяargumentsобъекта следующим образом:
function mixArgs(first, second) {
'use strict'
console.log(arguments[0]) // A
console.log(arguments[1]) // B
first = 'a'
second = 'b'
console.log(arguments[0]) // A
console.log(arguments[1]) // B
}
mixArgs('A', 'B')
для использованияES6Значение по умолчанию формального параметра,argumentsПоведение объекта всегда поддерживает иES5Строгий режим одинаков независимо от того, находится ли он в данный момент в строгом режиме, то есть:argumentsвсегда равен первоначально переданному значению, не изменяется при изменении параметра, всегда может быть использованargumentsОбъект восстанавливает исходное значение параметра следующим образом:
function mixArgs(first, second = 'B') {
console.log(arguments.length) // 1
console.log(arguments[0]) // A
console.log(arguments[1]) // undefined
first = 'a'
second = 'b'
console.log(arguments[0]) // A
console.log(arguments[1]) // undefined
}
// arguments对象始终等于传递的值,形参默认值不会反映在arguments上
mixArgs('A')
Выражение параметра по умолчанию
Значение параметра функции по умолчанию, помимо значения по умолчанию исходного значения, также может быть выражением, то есть переменной, и вызов функции также допустим.
function getValue () {
return 5
}
function add (first, second = getValue()) {
return first + second
}
console.log(add(1, 1)) // 2
console.log(add(1)) // 6
Анализ кода: Когда мы звоним в первый разadd(1,1)функция, так как значение параметра по умолчанию не используется, поэтомуgetValueи не будет называться. только если мы используемsecondКогда значение параметра по умолчаниюadd(1),getValueбудет вызвана функция.
Поскольку параметры по умолчанию оцениваются при вызове функции, мы можем использовать ранее определенные параметры в выражениях параметров, определенных позже, то есть мы можем рассматривать ранее определенные параметры как переменные или параметры вызова функции следующим образом:
function getValue(value) {
return value + 5
}
function add (first, second = first + 1) {
return first + second
}
function reduce (first, second = getValue(first)) {
return first - second
}
console.log(add(1)) // 3
console.log(reduce(1)) // -5
Временная зона нечувствительности для параметров по умолчанию
упомянутый ранееletа такжеconstИмеется временная мертвая зона, т.е.: вletа такжеconstПопытка доступа к переменной до ее объявления вызывает ошибку. По этой же причине в параметрах функции по умолчанию также есть временная мертвая зона, а именно:
function add (first = second, second) {
return first + second
}
add(1, 1) // 2
add(undefined, 1) // 抛出错误
Анализ кода: при первом звонкеadd(1,1), мы передаем два параметра, затемaddФункция не будет использовать значение параметра по умолчанию; при втором вызовеadd(undefined, 1)когда мы даемfirstпараметр переданundefined,ноfirstПараметр использует значение параметра по умолчанию, и на этот разsecondПеременная не была инициализирована, поэтому выдается ошибка.
неопределенный параметр
JavaScriptСинтаксис функции предусматривает, что независимо от того, сколько именованных параметров определено функцией, количество фактических параметров, передаваемых при вызове, не ограничено. существуетES6В , когда передается меньше параметров, для обработки используются значения параметров по умолчанию, когда передается большее количество параметров, для обработки используются неопределенные параметры.
мы начинаем сunderscore.jsв библиотекеpickМетод например:
pick方法的用法是:给定一个对象,返回指定属性的对象的副本。
function pick(object) {
let result = Object.create(null)
for (let i = 1, len = arguments.length; i < len; i++) {
let item = arguments[i]
result[item] = object[item]
}
return result
}
const book = {
title: '深入理解ES6',
author: '尼古拉斯',
year: 2016
}
console.log(pick(book, 'title', 'author')) // { title: '深入理解ES6', author: '尼古拉斯' }
Анализ кода:
- Нелегко обнаружить, что эта функция может принимать любое количество аргументов.
- Когда вам нужно найти копируемое свойство, вы должны начать с индекса 1.
существуетES6Неопределенные параметры представлены в , мы можем использовать характеристики неопределенных параметров для перезаписиpickфункция:
function pick(object, ...keys) {
let result = Object.create(null)
for (let i = 0, len = keys.length; i < len; i++) {
let item = keys[i]
result[item] = object[item]
}
return result
}
const book = {
title: '深入理解ES6',
author: '尼古拉斯',
year: 2016
}
console.log(pick(book, 'title', 'author')) // { title: '深入理解ES6', author: '尼古拉斯' }
Ограничения на неопределенные параметры
Есть несколько ограничений на использование неопределенных параметров:
- Функция может иметь не более одного неопределенного параметра.
- Неопределенные параметры должны быть помещены в конце всех параметров.
- нельзя использовать в литералах объектов
setterиспользовать неопределенные параметры.
// 报错,只能有一个不定参数
function add(first, ...rest1, ...rest2) {
console.log(arguments)
}
// 报错,不定参数只能放在最后一个参数
function add(first, ...rest, three) {
console.log(arguments)
}
// 报错,不定参数不能用在对象字面量`setter`之中
const object = {
set name (...val) {
console.log(val)
}
}
спред оператор
существуетES6Среди новых функций оператора спреда наиболее похож на неопределенный параметр.Неопределенный параметр позволяет нам указать несколько независимых параметров и получить к ним доступ через интегрированный массив; оператор спреда позволяет указать массив, который будет после того, как они разбиты, они передаются в функцию как свои собственные независимые параметры.
существуетES6Раньше, если мы использовалиMath.maxФункцию, которая сравнивает максимальное значение в массиве, нужно использовать следующим образом:
const arr = [4, 10, 5, 6, 32]
console.log(Math.max.apply(Math, arr)) // 32
Анализ кода: вES6Раньше не было проблем с использованием этого метода, но ключевой момент в том, что мы должны заимствоватьapplyметод, и с ним следует обращаться с особой осторожностьюthis(первый аргумент), вES6У нас есть более простые способы достижения вышеуказанных целей:
const arr = [4, 10, 5, 6, 32]
console.log(Math.max(...arr)) // 32
атрибут имени функции
В: почемуES6будет введена функцияnameАтрибуты.
Ответ: вJavaScriptСуществует несколько способов определения функций, поэтому идентификация функций является сложной задачей. Кроме того, широкое использование выражений анонимных функций также усложняет отладку. Чтобы решить эти проблемы, вESCAScript 6Добавлено для всех функций вnameАтрибуты.
Общий атрибут имени
В объявлениях функций и выражениях анонимных функцийnameСвойства относительно фиксированы:
function doSomething () {
console.log('do something')
}
let doAnotherThing = function () {
console.log('do another thing')
}
console.log(doSomething.name) // doSomething
console.log(doAnotherThing.name) // doAnotherThing
Особый случай атрибута name
Хотя легко определить имена объявлений функций и выражений функций, есть и другие случаи, которые не так просто идентифицировать:
- Выражения анонимных функций показывают случай, когда предоставляется имя функции: само имя функции имеет более высокий вес, чем переменная, которой назначена сама функция.
- Литерал объекта: если имя функции не указано, берется имя литерала объекта; если указано имя функции, это предоставленное имя.
- атрибут
getterа такжеsetter: существует на объектеget + 属性изgetилиsetметод. - пройти через
bind:пройти черезbindфункция, созданная функцией,nameиметьboundприставка - Через конструктор: имя функции фиксируется как
anonymous.
let doSomething = function doSomethingElse () {
console.log('do something else')
}
let person = {
// person对象上存在name为get firstName的方法
get firstName () {
return 'why'
},
sayName: function () {
console.log('why')
},
sayAge: function sayNewAge () {
console.log(23)
}
}
console.log(doSomething.name) // doSomethingElse
console.log(person.sayName.name) // sayName
console.log(person.sayAge.name) // sayNewAge
console.log(doSomething.bind().name) // bound doSomethingElse
console.log(new Function().name) // anonymous
Многократное использование функций
существуетJavaScriptСредняя функция имеет несколько функций, которые можно комбинировать.newиспользовать внутри функцииthisЗначение указывает на новый объект, и функция в конечном итоге вернет этот новый объект следующим образом:
function Person (name) {
this.name = name
}
const person = new Person('why')
console.log(person.toString()) // [object Object]
существуетES6, функция имеет два разных внутренних метода, а именно:
具有[[Construct]]方法的函数被称为构造函数,但并不是所有的函数都有[[Construct]]方法,例如:箭头函数。
-
[[Call]]: если не прошелnewключевое слово для вызова функции, затем выполнить[[Call]]функция, которая непосредственно выполняет тело функции в коде. -
[[Construct]]: когда прошлоnewКогда ключевое слово вызывает функцию, выполнение[[Construct]]функция, которая отвечает за создание нового объекта и последующее выполнение тела функции, котораяthisПривязать к экземпляру.
существуетES6Раньше, если вы хотели определить, прошла ли функцияnewвызов ключевого слова, наиболее популярным методом является использованиеinstanceofсудить, например:
function Person (name) {
if (this instanceof Person) {
this.name = name
} else {
throw new Error('必须通过new关键词来调用Person')
}
}
const person = new Person('why')
const notPerson = Person('why') // 抛出错误
Анализ кода: в этом коде мы сначала оценимthisзначение, чтобы увидеть, если этоPersonэкземпляр , продолжить выполнение, если это так, выдать ошибку, если нет. Вообще говоря, такой подход правильный, но не очень надежный, есть способ не полагаться наnewКлючевые слова также могут бытьthisсвязываются сPersonнапример, следующим образом:
function Person (name) {
if (this instanceof Person) {
this.name = name
} else {
throw new Error('必须通过new关键词来调用Person')
}
}
const person = new Person('why')
const notPerson = Person.call(person, 'why') // 不报错,有效
Чтобы решить проблему оценки того, проходит ли функцияnewПроблема вызова ключевого слова,ES6представилnew.targetэто мета-свойство
Вопрос. Что такое мета-свойства?
О: Мета-атрибут относится к свойству не-объекта, которое может предоставить дополнительную информацию о нецелевом объекте. Когда функция вызывается[[Construct]]метод,new.targetназначается какnewЦель оператора, обычно экземпляр только что созданного объекта, то есть тело функции.thisконструктор; если вы позвоните[[Call]]метод, тоnew.targetценностьundefined.
Согласно вышеизложенномуnew.targetОсобенности, переписываем приведенный выше код:
在函数外使用new.target是一个语法错误。
function Person (name) {
if (typeof new.target !== 'undefined') {
this.name = name
} else {
throw new Error('必须通过new关键词来调用Person')
}
}
const person = new Person('why')
const notPerson = Person.call(person, 'why') // 抛出错误
блочная функция
существуетECMAScript 3А в более ранних версиях объявление функции блочного уровня в блоке кода является строго синтаксической ошибкой, но все браузеры по-прежнему поддерживают эту функцию, но степень поддержки немного отличается из-за различий браузеров, поэтомуЛучше не использовать эту функцию, если вы хотите использовать выражения анонимных функций..
// ES5严格模式下,在代码块中声明一个函数会报错
// 在ES6下,因为有了块级作用域的概念,所以无论是否处于严格模式,都不会报错。
// 但在ES6中,当处于严格模式时,会将函数声明提升至当前块级作用域的顶部
// 当处于非严格模式时,提升至外层作用域
'use strict'
if (true) {
function doSomething () {
console.log('do something')
}
}
стрелочная функция
существуетES6, стрелочные функции — одно из самых интересных новых дополнений, стрелочные функции — это способ использования стрелок=>Новый синтаксис для определения функций, но он такой же, как и традиционный.JavaScriptФункция немного отличается:
-
нет this, super, аргументов и привязок new.target: в функции стрелки
this,super,argumentsа такжеnew.targetЭти значения определяются ближайшим слоем нестрелочных функций на периферии. -
Невозможно вызвать с новым ключевым словом: потому что стрелочные функции не имеют
[[Construct]]функция, поэтому не может пройтиnewключевое слово для вызова, если используетсяnewПри звонке выдает ошибку. -
нет прототипа: потому что это не пройдет
newключевое слово для вызова, поэтому нет необходимости создавать прототип и нетprototypeэто свойство. -
Привязка этого не может быть изменена: внутри функции стрелки,
thisЗначение является неизменным (то есть не может быть переданоcall,applyилиbindд., чтобы изменить). -
объект аргумента не поддерживается: функция стрелки нет
argumentsBinding, поэтому для доступа к параметрам необходимо использовать либо именованные параметры, либо неопределенные параметры. - Повторяющиеся именованные параметры не поддерживаются: Стрелочные функции не поддерживают повторяющиеся именованные аргументы, независимо от того, в строгом режиме они или нет.
Синтаксис стрелочных функций
Синтаксис стрелочных функций является переменным, и существуют различные формы в соответствии с реальными сценариями использования. Все варианты состоят из параметров функции, стрелок и тела функции.
Одно из проявлений:
// 表现形式之一:没有参数
let reflect = () => 5
// 相当于
let reflect = function () {
return 5
}
Вторая форма выражения:
// 表现形式之二:返回单一值
let reflect = value => value
// 相当于
let reflect = function (value) {
return value
}
Третья форма выражения:
// 表现形式之三:多个参数
let reflect = (val1, val2) => val1 + val2
// 或者
let reflect = (val, val2) => {
return val1 + val2
}
// 相当于
let reflect = function (val1, val2) {
return val1 + val2
}
Четвертая форма выражения:
// 表现形式之四:返回字面量
let reflect = (id) => ({ id: id, name: 'why' })
// 相当于
let reflect = function (id) {
return {
id: id,
name: 'why'
}
}
Стрелочные функции и массивы
Стрелочные функции имеют лаконичный синтаксис и отлично подходят для работы с массивами.
const arr = [1, 5, 3, 2]
// 非箭头函数排序写法
arr.sort(function(a, b) {
return a -b
})
// 箭头函数排序写法
arr.sort((a, b) => a - b)
оптимизация хвостового вызова
Хвостовой вызов — это когда функция вызывается как последний оператор другой функции.
Пример хвостового вызова:
function doSomethingElse () {
console.log('do something else')
}
function doSomething () {
return doSomethingElse()
}
существуетECMAScript 5Реализация хвостовых вызовов аналогична реализации других вызовов функций в движке .Фреймы хранятся в памяти, что может вызвать проблемы программирования, когда стек вызовов становится слишком большим.
Для вышеуказанных проблем, которые могут возникнуть,ES6Уменьшен размер стека хвостовых вызовов в строгом режиме.При выполнении всех следующих условий хвостовой вызов больше не будет создавать новый кадр стека, а будет очищать и повторно использовать текущий кадр стека:
- Хвостовой вызов не обращается к переменным текущего кадра стека (функция не является замыканием).
- Хвостовой вызов является последним оператором
- Результат хвостового вызова возвращается как функция
Пример хвостового вызова, удовлетворяющего вышеуказанным условиям:
'use strict'
function doSomethingElse () {
console.log('do something else')
}
function doSomething () {
return doSomethingElse()
}
Пример хвостового вызова, который не удовлетворяет вышеуказанным условиям:
function doSomethingElse () {
console.log('do something else')
}
function doSomething () {
// 无法优化,没有返回
doSomethingElse()
}
function doSomething () {
// 无法优化,返回值又添加了其它操作
return 1 + doSomethingElse()
}
function doSomething () {
// 可能无法优化
let result = doSomethingElse
return result
}
function doSomething () {
let number = 1
let func = () => number
// 无法优化,该函数是一个闭包
return func()
}
Рекурсивная функция является ее основным сценарием применения.Когда количество вычислений рекурсивной функции достаточно велико, оптимизация хвостового вызова может значительно повысить производительность программы.
// 优化前
function factorial (n) {
if (n <= 1) {
return 1
} else {
// 无法优化
return n * factorial (n - 1)
}
}
// 优化后
function factorial (n, p = 1) {
if (n <= 1) {
return 1 * p
} else {
let result = n * p
return factorial(n -1, result)
}
}
расширение объекта
Расширения литералов объектов
Расширение литерала объекта состоит из двух частей:
- Сокращение для начального значения свойства: Когда свойство объекта имеет то же имя, что и локальная переменная, нет необходимости писать двоеточие и значение, а просто писать свойство.
-
сокращение для методов объекта: устраняет толстую кишку и
functionключевые слова. - Имя вычисляемого свойства: При определении объекта значение свойства объекта может быть вычислено через переменную.
Методы, созданные с помощью сокращенного синтаксиса объектного метода, имеютnameАтрибут, значением которого является имя перед скобками.
const name = 'why'
const firstName = 'first name'
const person = {
name,
[firstName]: 'ABC',
sayName () {
console.log(this.name)
}
}
// 相当于
const name = 'why'
const person = {
name: name,
'first name': 'ABC',
sayName: function () {
console.log(this.name)
}
}
новый метод
Object.is
В использованииJavaScriptПри сравнении двух значений мы можем привыкнуть использовать==или===судить, использовать конгруэнтность===Он может избежать запуска приведения типов при сравнении, поэтому его любят многие люди. но конгруэнтный===И это не совсем точно, например:+0===-0вернусьtrue,NaN===NaNвернусьfalse. Для вышеуказанной ситуацииES6представилObject.isметод компенсации.
// ===和Object.is大多数情况下结果是相同的,只有极少数结果不同
console.log(+0 === -0) // true
console.log(Object.is(+0, -0)) // false
console.log(NaN === NaN) // false
console.log(Object.is(NaN, NaN)) // true
Object.assign
В: Что такоеMixin?
Ответ: смешанныйMixinдаJavaScriptОдин из самых популярных шаблонов реализации композиции объектов в . вmixin, объект принимает свойства и методы от другого объекта (mixinметод является поверхностной копией).
// mixin方法
function mixin(receiver, supplier) {
Object.keys(supplier).forEach(function(key) {
receiver[key] = supplier[key]
})
return receiver
}
const person1 = {
age: 23,
name: 'why'
}
const person2 = mixin({}, person1)
console.log(person2) // { age: 23, name: 'why' }
Поскольку этот режим наложения настолько популярен,ES6представилObject.assignметод для достижения той же функции, этот метод принимает объект принятия и любое количество исходных объектов и, наконец, возвращает объект принятия.
如果源对象中有同名的属性,后面的源对象会覆盖前面源对象中的同名属性。
const person1 = {
age: 23,
name: 'why'
}
const person2 = {
age: 32,
address: '广东广州'
}
const person3 = Object.assign({}, person1, person2)
console.log(person3) // { age: 32, name: 'why', address: '广东广州' }
Object.assign方法不能复制属性的get和set。
let receiver = {}
let supplier = {
get name () {
return 'why'
}
}
Object.assign(receiver, supplier)
const descriptor = Object.getOwnPropertyDescriptor(receiver, 'name')
console.log(descriptor.value) // why
console.log(descriptor.get) // undefined
console.log(receiver) // { name: 'why' }
Повторяющиеся литеральные свойства объекта
существуетECMAScript 5В строгом режиме добавление повторяющихся свойств к объекту вызовет ошибку:
'use strict'
const person = {
name: 'AAA',
name: 'BBB' // ES5环境触发错误
}
но вECMAScript 6, независимо от того, находится ли он в данный момент в строгом режиме, добавление повторяющегося атрибута не сообщит об ошибке, а выберет последнее значение:
'use strict'
const person = {
name: 'AAA',
name: 'BBB' // ES6环境不报错
}
console.log(person) // { name: 'BBB' }
Порядок перечисления собственного имущества
ES5Порядок перечисления свойств объекта не определен производителем браузера. пока вES6Порядок, в котором перечисляются собственные свойства объекта, строго указан в порядке возврата.
правило:
- Все цифровые клавиши отсортированы в порядке возрастания.
- Все символьные ключи сортируются в том порядке, в котором они были добавлены к объекту.
- все
SymbolКлючи сортируются в том порядке, в котором они были добавлены к объекту.
Согласно приведенным выше правилам, будут затронуты следующие методы:
-
Object.getOwnPropertyNames(). -
Reflect.keys(). -
Object.assign().
Неопределенный случай:
-
for-inЦикл по-прежнему нумеруется производителем для определения порядка. -
Object.keys()а такжеJSON.stringify()тоже самоеfor-inДля шлейфов порядок нумерации определяет производитель.
const obj = {
a: 1,
0: 1,
c: 1,
2: 1,
b: 1,
1: 1
}
obj.d = 1
console.log(Reflect.keys(obj).join('')) // 012acbd
Усовершенствованный прототип объекта
ES5, прототип объекта остается неизменным после создания экземпляра. пока вES6добавлено вObject.setPrototypeOf()способ изменить эту ситуацию.
const person = {
sayHello () {
return 'Hello'
}
}
const dog = {
sayHello () {
return 'wang wang wang'
}
}
let friend = Object.create(person)
console.log(friend.sayHello()) // Hello
console.log(Object.getPrototypeOf(friend) === person) // true
Object.setPrototypeOf(friend, dog)
console.log(friend.sayHello()) // wang wang wang
console.log(Object.getPrototypeOf(friend) === dog) // true
Суперссылки для упрощения доступа к прототипам
существуетES5, если мы хотим переопределить метод экземпляра объекта, но также нужно вызвать метод прототипа с тем же именем, что и у него, мы можем сделать следующее:
const person = {
sayHello () {
return 'Hello'
}
}
const dog = {
sayHello () {
return 'wang wang wang'
}
}
const friend = {
sayHello () {
return Object.getPrototypeOf(this).sayHello.call(this) + '!!!'
}
}
Object.setPrototypeOf(friend, person)
console.log(friend.sayHello()) // Hello!!!
console.log(Object.getPrototypeOf(friend) === person) // true
Object.setPrototypeOf(friend, dog)
console.log(friend.sayHello()) // wang wang wang!!!
console.log(Object.getPrototypeOf(friend) === dog) // true
Анализ кода: точно помните, как использоватьObject.getPrototypeOf()а такжеxx.call(this)метод для вызова метода на прототипе действительно немного сложен. А в случае множественного наследованияObject.getPrototypeOf()Проблемы возникнут.
Согласно приведенным выше вопросам,ES6представилsuperключевые слова, которыеsuperЭквивалентно указателю на прототип объекта, поэтому приведенный выше код можно изменить следующим образом:
super关键字只出现在对象简写方法里,普通方法中使用会报错。
const person = {
sayHello () {
return 'Hello'
}
}
const dog = {
sayHello () {
return 'wang wang wang'
}
}
const friend = {
sayHello () {
return super.sayHello.call(this) + '!!!'
}
}
Object.setPrototypeOf(friend, person)
console.log(friend.sayHello()) // Hello!!!
console.log(Object.getPrototypeOf(friend) === person) // true
Object.setPrototypeOf(friend, dog)
console.log(friend.sayHello()) // wang wang wang!!!
console.log(Object.getPrototypeOf(friend) === dog) // true
Формальное определение метода
существуетES6Понятие «метод» никогда ранее не определялось формально, метод — это просто свойство объекта с функциональностью, а не данные. пока вES6Формально метод определяется как функция в , которая будет иметь внутреннюю[[HomeObject]]свойство для хранения объекта, от которого зависит этот метод.
const person = {
// 是方法 [[HomeObject]] = person
sayHello () {
return 'Hello'
}
}
// 不是方法
function sayBye () {
return 'goodbye'
}
Согласно вышеизложенному[[HomeObject]]правило, мы можем получитьsuperКак это работает:
- существует
[[HomeObject]]вызов на недвижимостьObject.getPrototypeOf()метод для получения ссылки на прототип. - Найдите в прототипе одноименную функцию.
- настраивать
thisПривяжите и вызовите соответствующий метод.
const person = {
sayHello () {
return 'Hello'
}
}
const friend = {
sayHello () {
return super.sayHello() + '!!!'
}
}
Object.setPrototypeOf(friend, person)
console.log(friend.sayHello()) // Hello!!!
Анализ кода:
-
friend.sayHello()метод[[HomeObject]]Стоимость свойстваfriend. -
friendПрототипperson. -
super.sayHello()эквивалентноperson.sayHello.call(this).
разрушать
Деструктуризация — это процесс разбиения структуры данных на более мелкие части.
Зачем использовать деструктурирование
существуетECMAScript 5В и более ранних версиях для того, чтобы получить определенные данные из объекта или массива и присвоить их переменной, было написано много вроде бы однородного кода:
const person = {
name: 'AAA',
age: 23
}
const name = person.name
const age = person.age
Анализ кода: мы должны начать сpersonизвлечение объектаnameа такжеageЗначение , и присвоение его значения соответствующей одноименной переменной, процесс очень похож. Предположим, мы хотим извлечь много переменных, этот процесс будет повторяться много раз, если есть еще и вложенные структуры, реальная информация не будет найдена простым обходом.
Для вышеуказанных проблем,ES6Вводится понятие деконструкции, которое можно разделить на:
- деструктуризация объекта
- деструктуризация массива
- Гибридная деконструкция
- Параметры деструктуризации
деструктуризация объекта
Мы используемES6Структура объекта в , переписав приведенный выше пример:
const person = {
name: 'AAA',
age: 23
}
const { name, age } = person
console.log(name) // AAA
console.log(age) // 23
必须为解构赋值提供初始化程序,同时如果解构右侧为null或者undefined,解构会发生错误。
// 以下代码为错误示例,会报错
var { name, age }
let { name, age }
const { name, age }
const { name, age } = null
const { name, age } = undefined
присваивание деструктуризации
Мы можем не только переопределить переменные во время деструктуризации, но также можем уничтожить и присвоить переменные, которые уже существуют:
const person = {
name: 'AAA',
age: 23
}
let name, age
// 必须添加(),因为如果不加,{}代表是一个代码块,而语法规定代码块不能出现在赋值语句的左侧。
({ name, age } = person)
console.log(name) // AAA
console.log(age) // 23
Разрушение значений по умолчанию
При использовании выражения назначения деконструкции, если указанное имя локальной переменной не существует в объекте, то эта частичная переменная будет назначенаundefined, можно дополнительно указать значение по умолчанию.
const person = {
name: 'AAA',
age: 23
}
let { name, age, sex = '男' } = person
console.log(sex) // 男
Присвоить значения переменным с разными именами
До сих пор, когда мы деконструировали присваивание, деконструируемый ключ и присваиваемая переменная имели одно и то же имя, но как деконструировать присваивание для переменных с разными именами?
const person = {
name: 'AAA',
age: 23
}
let { name, age } = person
// 相当于
let { name: name, age: age } = person
let { name: name, age: age } = personЗначение: вpersonКлюч в объектеnameа такжеage, и назначьте ихnameпеременная иageПеременная.
Тогда, согласно изложенным выше идеям, присвоение значений переменным с разными именами можно переписать следующим образом:
const person = {
name: 'AAA',
age: 23
}
let { name: newName, age: newAge } = person
console.log(newName) // AAA
console.log(newAge) // 23
Структура вложенных объектов
Деструктуризация вложенных объектов по-прежнему похожа на синтаксис литерала объекта, за исключением того, что мы можем деконструировать объект в то, что мы хотим.
const person = {
name: 'AAA',
age: 23,
job: {
name: 'FE',
salary: 1000
},
department: {
group: {
number: 1000,
isMain: true
}
}
}
let { job, department: { group } } = person
console.log(job) // { name: 'FE', salary: 1000 }
console.log(group) // { number: 1000, isMain: true }
let { job, department: { group } } = personЗначение: вpersonКлюч извлеченияjob,существуетpersonвложенный объектdepartmentКлюч извлеченияgroupзначение и присвоить его соответствующей переменной.
деструктуризация массива
Назначение деструктуризации массива похоже на синтаксис деструктуризации объекта, но намного проще. Он использует литерал массива, и все операции деструктуризации выполняются в массиве. Процесс деструктуризации основан на позиции значения в массив.
const colors = ['red', 'green', 'blue']
let [firstColor, secondColor] = colors
// 按需解构
let [,,threeColor] = colors
console.log(firstColor) // red
console.log(secondColor) // green
console.log(threeColor) // blue
Как и объект, деконструированный массив также может декорировать уже существующие переменные, но ему не нужно добавлять круглые скобки как объекту:
const colors = ['red', 'green', 'blue']
let firstColor, secondColor
[firstColor, secondColor] = colors
console.log(firstColor) // red
console.log(secondColor) // green
Согласно вышеуказанным принципам, мы можем легко расширить функцию деструктурной задачи (быстро поменяйте значения двух переменных):
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1
Как и объекты, деструктуризация массива также может устанавливать деструктуризацию по умолчанию:
const colors = ['red']
const [firstColor, secondColor = 'green'] = colors
console.log(firstColor) // red
console.log(secondColor) // green
Когда есть вложенные массивы, мы также можем использовать идею деструктурирования вложенных объектов для решения:
const colors = ['red', ['green', 'lightgreen'], 'blue']
const [firstColor, [secondColor]] = colors
console.log(firstColor) // red
console.log(secondColor) // green
неопределенный параметр
При деструктурировании массива неопределенный элемент может быть помещен только в конец, а добавление запятых после этого вызовет сообщение об ошибке.
В деструктуризации массива есть функция, аналогичная неопределенным параметрам функций: при деструктуризации массива можно использовать...Остальные элементы в синтаксисе массива присваиваются конкретной переменной:
let colors = ['red', 'green', 'blue']
let [firstColor, ...restColors] = colors
console.log(firstColor) // red
console.log(restColors) // ['green', 'blue']
В соответствии с изложенным выше принципом деструктуризации неопределенных элементов в массиве мы можем добиться того жеconcatТа же функция копирования массива:
const colors = ['red', 'green', 'blue']
const concatColors = colors.concat()
const [...restColors] = colors
console.log(concatColors) // ['red', 'green', 'blue']
console.log(restColors) // ['red', 'green', 'blue']
Параметры деструктуризации
Когда мы определяем функцию, которая должна принимать большое количество параметров, мы обычно создаем необязательный объект и определяем дополнительные параметры как свойства этого объекта:
function setCookie (name, value, options) {
options = options || {}
let path = options.path,
domain = options.domain,
expires = options.expires
// 其它代码
}
// 使用解构参数
function setCookie (name, value, { path, domain, expires } = {}) {
// 其它代码
}
Анализ кода:{ path, domain, expires } = {}Необходимо указать значение по умолчанию. Если значение по умолчанию не указано, передача третьего параметра приведет к ошибке:
function setCookie (name, value, { path, domain, expires }) {
// 其它代码
}
// 报错
setCookie('type', 'js')
// 相当于解构了undefined,所以会报错
{ path, domain, expires } = undefined
Символ и его свойства Символ
существуетES6До,JavaScriptЯзыки имеют всего пять примитивных типов:string,number,boolean,nullа такжеundefiend. существуетES6, был добавлен шестой примитивный тип:Symbol.
можно использоватьtypeofобнаруживатьSymbolТипы:
const symbol = Symbol('Symbol Test')
console.log(typeof symbol) // symbol
Создать символ
через глобальныйSymbolфункция для созданияSymbol.
const firstName = Symbol()
const person = {}
person[firstName] = 'AAA'
console.log(person[firstName]) // AAA
допустимыйSymbol()Передача необязательного параметра позволяет нам добавить фрагмент текста, описывающий то, что мы создали.Symbol, где текст хранится во внутреннем свойстве[[Description]], только при звонкеSymbolизtoString()Этот метод может быть атрибутом только для чтения.
const firstName = Symbol('Symbol Description')
const person = {}
person[firstName] = 'AAA'
console.log(person[firstName]) // AAA
console.log(firstName) // Symbol('Symbol Description')
Использование символов
Везде, где вы можете использовать имя вычисляемого свойства, вы можете использоватьSymbol.
let firstName = Symbol('first name')
let lastName = Symbol('last name')
const person = {
[firstName]: 'AAA'
}
Object.defineProperty(person, firstName, {
writable: false
})
Object.defineProperties(person, {
[lastName]: {
value: 'BBB',
writable: false
}
})
console.log(person[firstName]) // AAA
console.log(person[lastName]) // BBB
Система обмена символами
ES6Предоставляет глобальный объект, к которому можно получить доступ в любое время.Symbolреестр, чтобы позволить нам создавать общие ресурсыSymbolвозможность использоватьSymbol.for()Метод создания общегоSymbol.
// Symbol.for方法的参数,也被用做Symbol的描述内容
const uid = Symbol.for('uid')
const object = {
[uid]: 12345
}
console.log(person[uid]) // 12345
console.log(uid) // Symbol(uid)
Анализ кода:
-
Symbol.for()Сначала метод будет в глобальномSymbolКлюч поиска в регистрационной переменнойuidизSymbolон существует. - существует, напрямую возвращает существующий
Symbol. - не существует, создайте новый
Symbol, и используйте этот ключ вSymbolГлобальная регистрация изменяется на регистрацию, а затем возвращает только что созданнуюSymbol.
и еще одинSymbolЧтобы поделиться связанными функциями, вы можете использоватьSymbol.keyFor()метод вSymbolПоиск в глобальном реестре с помощьюSymbolРассматриваемый ключ, если он существует, возвращается, если он не существуетundefined:
const uid = Symbol.for('uid')
const uid1 = Symbol('uid1')
console.log(Symbol.keyFor(uid)) // uid
console.log(Symbol.keyFor(uid1)) // undefined
Приведение символов и типов
Другие примитивные типы не связаны сSymbolлогически равные значения, особенно еслиSymbolПриведение к строкам и числам.
const uid = Symbol.for('uid')
console.log(uid)
console.log(String(uid))
// 报错
uid = uid + ''
uid = uid / 1
Анализ кода: мы используемconsole.log()метод печатиSymbol, который вызоветSymbolизString()метод, поэтому его также можно вызвать напрямуюString()вывод методаSymbol. Тем не менее, попробуйтеSymbolОбъединение со строкой приведет к тому, что программа выдаст исключение,SymbolЕго также нельзя смешивать с каждым математическим оператором, иначе тоже будет выдана ошибка.
Извлечение свойства символа
Object.keys()а такжеObject.getOwnPropertyNames()Метод может получить все имена свойств в объекте, среди которыхObject.keysвозвращает все перечисляемые свойства,Object.getOwnPropertyNames()Возвращает, является ли свойство перечислимым или нет, но ни один из методов не возвращаетSymbolАтрибуты. следовательноES6представил новый методObject.getOwnPropertySymbols()метод.
const uid = Symbol.for('uid')
let object = {
[uid]: 123
}
const symbols = Object.getOwnPropertySymbols(object)
console.log(symbols.length) // 1
console.log(symbols[0]) // Symbol(uid)
Символ раскрывает внутренние операции
ES6Определив цепочку прототипов с помощьюSymbolСвязанные свойства, чтобы раскрыть больше внутренней логики языка, эти внутренние операции следующие:
-
Symbol.hasInstance: Один выполняетinstanceofВнутренний метод, вызываемый, когда объект используется для обнаружения сведений о наследовании объекта. -
Symbol.isConcatSpreadable: логическое значение, используемое для указания, когда коллекция передается какArray.prototype.concat()Когда параметр метода, должны ли элементы в коллекции быть нормализованы до одного уровня. -
Symbol.iterator: метод, возвращающий итератор. -
Symbol.match: один звонитString.prototype.match()Метод для вызова, когда метод используется для сравнения строк. -
Symbol.replace: один звонитString.prototype.replace()Метод, вызываемый при замене подстрок в строке. -
Symbol.search: один звонитString,prototype.search()метод, вызываемый при поиске подстроки в строке. -
Symbol.split: один звонитString.prototype.split()Метод, вызываемый, когда метод используется для разделения строки. -
Symbol.species: Конструктор, используемый для создания производных объектов. -
Symbol.toPrimitive: метод, который возвращает исходное значение объекта. -
Symbol.toStringTag: один звонитObject.prototype.toString()Строка, используемая при использовании метода для создания описания объекта. -
Symbol.unscopables: человек определяет то, что не может бытьwithКоллекция объектов, имена свойств которых указаны в инструкции.
переписатьwell-known SymbolОпределенный метод вызывает изменение поведения по умолчанию внутри объекта, так что обычный объект становится сингулярным объектом.
Symbol.hasInstance
Каждая функция имеетSymbol.hasInstanceМетод для определения того, является ли объект экземпляром функции, и метод не является перечисляемым, недоступным для записи и конфигурируемым.
function MyObject () {
// 空函数
}
Object.defineProperty(MyObject, Symbol.hasInstance, {
value: function () {
return false
}
})
let obj = new MyObject()
console.log(obj instanceof MyObject) // false
Анализ кода: использованиеObject.definePropertyметод, вMyObjectпереписать функциюSymbol.hasInstance, для которого определение a всегда возвращаетfalseновую функцию, даже еслиobjконечноMyObject, но еще в процессеinstanceofвернулся при оценкеfalse.
注意如果要触发Symbol.hasInstance调用,instanceof的左操作符必须是一个对象,如果为非对象则会导致instanceof始终返回false。
Symbol.isConcatSpreadable
существуетJavaScriptв массивеconcat()метод используется для объединения двух массивов:
const colors1 = ['red', 'green']
const colors2 = ['blue']
console.log(colors1.concat(colors2, 'brown')) // ['red', 'green', 'blue', 'brown']
существуетconcat()мы передаем второй параметр, который не является элементом массива. еслиSymbol.isConcatSpreadableдляtrue, то объект имеетlengthсвойства и цифровые клавиши, поэтому его числовые клавиши добавляются независимо кconcatВ результате вызова это необязательное свойство объекта, улучшающееconcatФункция метода, эффективно упрощающая его свойства по умолчанию:
const obj = {
0: 'hello',
1: 'world',
length: 2,
[Symbol.isConcatSpreadable]: true
}
const message = ['Hi'].concat(obj)
console.log(message) // ['Hi', 'hello', 'world']
Symbol.match, Symbol.replace, Symbol.search, Symbol.split
существуетJavaScriptВ , строки и регулярные выражения часто появляются вместе, особенно несколько методов строкового типа могут принимать регулярные выражения в качестве параметров:
-
match: определяет, соответствует ли данная строка регулярному выражению. -
replace: заменить часть строки, которая соответствует регулярному выражению, заданной строкой. -
search: находит индекс позиции в строке, которая соответствует регулярному выражению. -
split: Разделите строку в соответствии с элементами, соответствующими регулярному выражению, и сохраните разделенный результат в массиве.
существуетES6Ранее вышеуказанные методы не могли использовать наши собственные определенные объекты для замены регулярных выражений для сопоставления строк, но вES6После этого введение нескольких методов, соответствующих изложенным вышеSymbol, встроенный языкRegexСобственные функции объекта полностью переданы на аутсорсинг.
const hasLengthOf10 = {
[Symbol.match] (value) {
return value.length === 10 ? [value] : null
},
[Symbol.replace] (value, replacement) {
return value.length === 10 ? replacement : value
},
[Symbol.search] (value) {
return value.length === 10 ? 0 : -1
},
[Symbol.split] (value) {
return value.length === 10 ? [,] : [value]
}
}
const message1 = 'Hello world'
const message2 = 'Hello John'
const match1 = message1.match(hasLengthOf10)
const match2 = message2.match(hasLengthOf10)
const replace1 = message1.replace(hasLengthOf10)
const replace2 = message2.replace(hasLengthOf10, 'AAA')
const search1 = message1.search(hasLengthOf10)
const search2 = message2.search(hasLengthOf10)
const split1 = message1.split(hasLengthOf10)
const split2 = message2.split(hasLengthOf10)
console.log(match1) // null
console.log(match2) // [Hello John]
console.log(replace1) // Hello world
console.log(replace2) // AAA
console.log(search1) // -1
console.log(search2) // 0
console.log(split1) // [Hello John]
console.log(split2) // [,]
Symbol.toPrimitive
Symbol.toPrimitiveМетоды определены в прототипе каждого стандартного типа и указывают, что следует делать при преобразовании объекта в значение-примитив.Symbol.toPrimitiveметод и передать значение в качестве параметра.
Для большинства стандартных объектов числовые схемы имеют следующие свойства в порядке приоритета:
- передача
valueOf()Метод, если результатом является исходное значение, возвращается. - В противном случае позвоните
toString()метод, который возвращается, если результатом является примитивное значение. - Выдает ошибку, если больше нет необязательных значений.
Также для большинства стандартных объектов строковые шаблоны имеют следующий конечный порядок уровней:
- передача
toString()метод, который возвращается, если результатом является примитивное значение. - В противном случае позвоните
valueOf()метод, который возвращается, если результатом является примитивное значение. - Выдает ошибку, если больше нет необязательных значений.
В большинстве случаев стандартный объект будет рассматривать режим по умолчанию как числовой режим (за исключениемDateобъект, и в этом случае шаблон по умолчанию будет обрабатываться как строковый шаблон), если пользовательскийSymbol.toPrimitiveметод, вы можете переопределить это поведение приведения по умолчанию.
function Temperature (degress) {
this.degress = degress
}
Temperature.prototype[Symbol.toPrimitive] = function (hint) {
switch (hint) {
case 'string':
return this.degress + '℃'
case 'number':
return this.degress
case 'default':
return this.deress + ' degress'
}
}
const freezing = new Temperature(32)
console.log(freezing + '') // 32 degress
console.log(freezing / 2) // 16
console.log(String(freezing)) // 32℃
Анализ кода: мы в объектахTemperatureпрототип переписанSymbol.toPrimitive, новый метод зависит от параметровhintУказанный шаблон возвращает разные значения, гдеhintпараметр поJavaScriptПоступление двигателя. в+оператор запускает режим по умолчанию,hintустановить какdefault;/оператор запускает числовой режим,hintустановить какnumber;String()Функция запускает шаблон строки,hintустановить какstring.
Symbol.toStringTag
существуетJavaScript, если у нас есть несколько глобальных сред выполнения одновременно, например, страница в браузере содержитiframeярлык, потому чтоiframeОн и его внешние страницы представляют собой разные поля. Каждое поле имеет свой глобальный объем и свой собственный глобальный объект. Массив, созданный в любом поле, является обычным массивом. Однако, если этот номер передан в другое поле,instanceof ArrayРезультат проверки оператора будет возвращенfalse,В настоящее времяArrayуже является конструктором другой области, по-видимому, инструментируемый массив не был создан этим конструктором.
В ответ на вышеуказанные проблемы мы быстро нашли относительно практичное решение:
function isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]'
}
console.log(isArray([])) // true
Аналогичный случай с приведенным выше вопросом, вES5Прежде чем мы сможем представить сторонние библиотеки для создания глобальныхJSONобъект, в то время как браузер начинает реализациюJSONПосле глобального объекта необходимо выделитьJSONобъектJavaScriptСамо окружение также предоставляется сторонними библиотеками:
function supportsNativeJSON () {
return typeof JSON !== 'undefined' && Object.prototype.toString.call(JSON) === '[object JSON]'
}
существуетES6в, черезSymbol.toStringTagэтоSymbolизменил вызовObject.prototype.toString()Идентификатор, возвращаемый, когда он определяет идентификатор вызывающего объекта.Object.prototype.toString.call()Значение, возвращаемое методом:
function Person (name) {
this.name = name
}
Person.prototype[Symbol.toStringTag] = 'Person'
const person = new Person('AAA')
console.log(person.toString()) // [object Person]
console.log(Object.prototype.toString.call(person)) // [object Person]
Коллекции наборов и карт
SetНабор — это список без повторяющихся элементов, обычно используемый для проверки того, входит ли заданное значение в набор;MapКоллекция содержит несколько наборов пар ключ-значение, и каждый элемент в коллекции хранит доступное имя ключа и соответствующее ему значение соответственно.MapКоллекции часто используются для кэширования часто используемых данных.
Коллекции Set и Map в ES5
существуетES6Официально еще не представленSetсбор иMapДо коллекций разработчики начали использовать свойства объекта для имитации этих двух коллекций:
const set = Object.create(null)
const map = Object.create(null)
set.foo = true
map.bar = 'bar'
// set检查
if (set.foo) {
console.log('存在')
}
// map取值
console.log(map.bar) // bar
Вышеупомянутая программа очень проста, и действительно возможно использовать свойства объекта для моделирования.Setсбор иMapсбор, но есть много неудобств в процессе реального использования:
- Имена свойств объекта должны быть строками:
const map = Object.create(null)
map[5] = 'foo'
// 本意是使用数字5作为键名,但被自动转换为了字符串
console.log(map['5']) // foo
- Объекты не могут использоваться в качестве имен свойств:
const map = Object.create(null)
const key1 = {}
const key2 = {}
map[key1] = 'foo'
// 本意是使用key1对象作为属性名,但却被自动转换为[object Object]
// 因此map[key1] = map[key2] = map['[object Object]']
console.log(map[key2]) // foo
- Неконтролируемое принуждение:
const map = Object.create(null)
map.count = 1
// 本意是检查count属性是否存在,实际检查的确是map.count属性的值是否为真
if (map.count) {
console.log(map.count)
}
Установить коллекцию в ES6
SetМножество — это упорядоченный список, содержащий несколько независимых друг от друга значений,SetВ коллекции не выполняются приведения сохраненных значений.
вSetОн включает в себя набор атрибутов и методов:
-
SetКонструктор: вы можете использовать этот конструктор для созданияSetсобирать. -
addметод: можетSetДобавляет элемент в коллекцию. -
deleteМетод: можно удалитьSetэлемент коллекции. -
clearМетод: можно удалитьSetВсе элементы коллекции. -
hasМетод: определить, находится ли данный элемент вSetв коллекции. -
sizeАтрибуты:SetДлина коллекции.
Создать коллекцию сетов
SetКонструктор коллекции может принимать в качестве параметра любой итерируемый объект, например: массив,Setколлекция илиMapсобирать.
const set = new Set()
set.add(5)
set.add('5')
// 重复添加的值会被忽略
set.add(5)
console.log(set.size) // 2
удалить элемент
использоватьdelete()метод для удаления значения из коллекции, используйтеclear()метод для удаления всех элементов из коллекции.
const set = new Set()
set.add(5)
set.add('5')
console.log(set.has(5)) // true
set.delete(5)
console.log(set.has(5)) // false
console.log(set.size) // 1
set.clear()
console.log(set.size) // 0
forEach() метод коллекции Set
SetколлекцияforEach()методы и массивыforEach()Метод тот же, разница только в том, чтоSetПри обходе коллекции первый и второй параметры совпадают.
const set = new Set([1, 2])
set.forEach((value, key, arr) => {
console.log(`${value} ${key}`)
console.log(arr === set)
})
// 1 1
// true
// 2 2
// true
Установить коллекцию, преобразованную в массив
потому чтоSetКоллекции не могут обращаться к элементам по индексу, как массивы. Лучший способ сделать это —SetНаборы преобразуются в массивы.
const set = new Set([1, 2, 3, 4])
// 方法一:展开运算符
const arr1 = [...set]
// 方法二:Array.from方法
const arr2 = Array.from(set)
console.log(arr1) // [1, 2, 3, 4]
console.log(arr2) // [1, 2, 3, 4]
Коллекция слабых наборов
через вышеуказанноеSetПрочесывая коллекцию, мы можем найти: покаSetСсылка в экземпляре существует, механизм сборки мусора не может освободить место в памяти объекта, поэтому ставимSetКоллекция рассматривается как коллекция с сильными ссылками. для лучшей обработкиSetСборка мусора вводит метод, называемыйWeak SetКоллекция:
Weak Set集合只支持三种方法:add、has和delete。
const weakSet = new WeakSet()
const key = {}
weakSet.add(key)
console.log(weakSet.has(key)) // true
weakSet.delete(key)
console.log(weakSet.has(key)) // false
Setсбор иWeak SetКоллекции имеют много общих свойств, но между ними есть некоторые различия:
-
Weak SetКоллекция может хранить только объектные элементы, добавление в нее необъектных элементов приведет к возникновению ошибки и аналогичным образом.has()а такжеdelete()Передача не-объекта также вызовет ошибку. -
Weak SetКоллекции не являются итерируемыми и не предоставляют никаких итераторов, поэтому не поддерживаются.forEach()метод. -
Weak SetКоллекция не поддерживаетсяsizeАтрибуты.
Коллекция карт в ES6
ES6серединаMapТип представляет собой упорядоченный список, в котором хранится множество пар ключ-значение. Имена ключей и соответствующие значения поддерживают все типы данных. Оценка эквивалентности имен ключей выполняется путем вызоваObject.isметод достижения.
const map = new Map()
const key1 = {
name: 'key1'
}
const key2 = {
name: 'key2'
}
map.set(5, 5)
map.set('5', '5')
map.set(key1, key2)
console.log(map.get(5)) // 5
console.log(map.get('5')) // '5'
console.log(map.get(key1)) // {name:'key2'}
Методы, поддерживаемые коллекцией Map
а такжеSetколлекция похожа,MapКоллекции также поддерживают следующие методы:
-
has: определить, находится ли указанное имя ключа вMapесть в коллекции. -
delete:существуетMapУдаляет указанное имя ключа и соответствующее ему значение из коллекции. -
clear: УдалитьMapВсе пары ключ-значение в коллекции.
const map = new Map()
map.set('name', 'AAA')
map.set('age', 23)
console.log(map.size) // 2
console.log(map.has('name')) // true
console.log(map.get('name')) // AAA
map.delete('name')
console.log(map.has('name')) // false
map.clear()
console.log(map.size) // 0
Метод инициализации коллекции Map
в инициализацииMapПри сборке также можно сделать такSetКоллекция передается в массив, но каждый элемент массива является вложенным массивом, а вложенный массив содержит два элемента: имя ключа и значение пары ключ-значение.
const map = new Map([['name', 'AAA'], ['age', 23]])
console.log(map.has('name')) // true
console.log(map.has('age')) // true
console.log(map.size) // 2
console.log(map.get('name')) // AAA
console.log(map.get('age')) // 23
forEach() метод сбора карт
Mapв коллекцииforEach()Параметры обратного вызова метода аналогичны массиву, и объяснение каждого параметра выглядит следующим образом:
- Первый параметр - это имя ключа
- Второй параметр – это значение
- Третий параметр
Mapсама коллекция
const map = new Map([['name', 'AAA'], ['age', 23]])
map.forEach((key, value, ownMap) => {
console.log(`${key} ${value}`)
console.log(ownMap === map)
})
// name AAA
// true
// age 23
// true
Слабая коллекция карт
Weak MapЭто неупорядоченный список, в котором хранится множество пар "ключ-значение". Имя ключа в коллекции должно быть объектом. Если используется имя ключа, не являющееся объектом, будет сообщено об ошибке.
Weak Map集合只支持set()、get()、has()和delete()。
const key1 = {}
const key2 = {}
const key3 = {}
const weakMap = new WeakMap([[key1, 'AAA'], [key2, 23]])
weakMap.set(key3, '广东')
console.log(weakMap.has(key1)) // true
console.log(weakMap.get(key1)) // AAA
weakMap.delete(key1)
console.log(weakMap.has(key)) // false
Mapсбор иWeak MapКоллекции имеют много общих свойств, но между ними есть некоторые различия:
-
Weak MapИмя ключа коллекции должно быть объектом. Добавление не-объекта приведет к ошибке. -
Weak MapКоллекции не повторяются, поэтому не поддерживаютсяforEach()метод. -
Weak MapКоллекция не поддерживаетсяclearметод. -
Weak MapКоллекция не поддерживаетсяsizeАтрибуты.
Итераторы и генераторы
проблема оператора цикла
В нашем ежедневном процессе разработки мы, возможно, написали следующий код:
var colors = ['red', 'gree', 'blue']
for(var i = 0, len = colors.length; i < len; i++) {
console.log(colors[i])
}
// red
// green
// blue
Анализ кода: хотя синтаксис оператора цикла прост, если несколько циклов вложены друг в друга, необходимо отслеживать несколько переменных, и сложность кода значительно возрастет.forпеременные отслеживания циклов, вызывающие ошибки программы, в то время какES6Цель введения итераторов — устранить эту сложность и уменьшить количество ошибок в циклах.
что такое итератор
Вопрос: Что такое итератор?
Ответ: Итератор — это объект особого типа, который имеет некоторые проприетарные интерфейсы, специально разработанные для итеративного процесса.nextметод, который возвращает объект результата при каждом вызове. Объект результата имеет два свойства, одно из которыхvalueУказывает, что значение, которое нужно возвращено дальше; другойdone, которое является логическим значением, которое возвращается, когда нет больше данных для возвратаtrue. Итератор также содержит внутренний указатель на позицию значения в текущей коллекции каждый раз, когда он вызывается.nextметод, вернет следующее доступное значение.
После понимания концепции итераторов мы используемES5синтаксис для создания итератора:
function createIterator (items) {
var i = 0
return {
next: function () {
var done = i >= items.length
var value = !done ? items[i++] : undefined
return {
done: done,
value: value
}
}
}
}
var iterator = createIterator([1, 2, 3])
console.log(iterator.next()) // { value: 1, done: false }
console.log(iterator.next()) // { value: 2, done: false }
console.log(iterator.next()) // { value: 3, done: false }
console.log(iterator.next()) // { value: undefined, done: true }
Как и выше, мы использовалиES5синтаксис для создания нашего собственного итератора, его внутренняя реализация сложна, иES6Помимо введения концепции итераторов, мы также вводим концепцию, называемую генераторами, с помощью которой мы можем немного упростить процесс создания итераторов.
что такое генератор
В: Что такое генератор?
Ответ: Генератор — это функция, которая возвращает итератор черезfunctionпосле ключевого слова*знак, указывающий, что в функции будут использоваться новые ключевые словаyield.
function * createIterator () {
yield 1
yield 2
yield 3
}
const iterator = createIterator()
console.log(iterator.next().value) // 1
console.log(iterator.next().value) // 2
console.log(iterator.next().value) // 3
Как наш вывод выше, это то же самое, что мы используемES5Вывод итераторов, созданных синтаксисом, является согласованным.
Самое главное в функциях-генераторах состоит в том, что каждое выполнениеyieldоператор, функция автоматически завершится:мы вES6Раньше, как только функция начинала выполняться, она продолжала выполняться вниз до тех пор, пока функцияreturnОператоры не прерываются, но функции-генераторы нарушают это соглашение: когдаyieldоператор, функция автоматически прекращает выполнение, если только код вручную не вызовет функцию итератора.nextметод.
Мы также можем использовать генераторы в циклах:
function * createIterator (items) {
for(let i = 0, len = items.length; i < len; i++) {
yield items[i]
}
}
const it = createIterator([1, 2, 3])
console.log(it.next()) // { done: false, value: 1 }
console.log(it.next()) // { done: false, value: 2 }
console.log(it.next()) // { done: false, value: 3 }
console.log(it.next()) // { done: true, value: undefined }
yield关键字只能在生成器内部使用,在其他地方使用会导致抛出错误,即使是在生成器内部的函数中使用也是如此。
function * createIterator (items) {
items.forEach(item => {
// 抛出错误
yield item + 1
})
}
Итерации и циклы for-of
В: Каковы характеристики итерируемых объектов?
Ответ: Итерируемые объекты имеютSymbol.iteratorСвойство — это объект, тесно связанный с итераторами.Symbol.iteratorУказанная функция возвращает итератор, который воздействует на прикрепленный объект. существуетES6, все объекты коллекции (массивы,Setсбор иMapколлекции) и строки являются итерируемыми объектами, в которых есть итераторы по умолчанию. Так как генератор по умолчаниюSymbol.iteratorприсвоение свойства, поэтому все итераторы, созданные генераторами, являются итерируемыми объектами.
ES6недавно представленныйfor-ofКаждый раз, когда цикл выполняется, итерируемый объектnextметод, а итератор возвращает объект результатаvalueСвойство сохраняется в переменной, и цикл будет продолжать выполнять этот процесс до тех пор, пока объект не будетdoneСтоимость имуществаtrue.
const value = [1, 2, 3]
for (let num of value) {
console.log(num);
}
// 1
// 2
// 3
получить доступ к итератору по умолчанию
в состоянии пройтиSymbol.iteratorдля доступа к итератору объекта по умолчанию
const values = [1, 2, 3]
const it = values[Symbol.iterator]()
console.log(it.next()) // {done:false, value:1}
console.log(it.next()) // {done:false, value:2}
console.log(it.next()) // {done:false, value:3}
console.log(it.next()) // {done:true, value:undefined}
из-за наличияSymbol.iteratorОбъекты свойств имеют объект итератора по умолчанию, поэтому вы можете использовать его, чтобы проверить, является ли объект итерируемым:
function isIterator (object) {
return typeof object[Symbol.iterator] === 'function'
}
console.log(isIterator([1, 2, 3])) // true
console.log(isIterator('hello')) // true,字符串也可以迭代,原理等同于数组
console.log(isIterator(new Set())) // true
console.log(isIterator(new Map)) // true
Создайте итерируемый объект
По умолчанию объекты, которые мы определяем сами, не являются итерируемыми объектами, но если мы дадимSymbol.iteratorсвойство для добавления генератора, вы можете превратить его в итерируемый объект.
let collection = {
items: [1, 2, 3],
*[Symbol.iterator] () {
for (let item of this.items) {
yield item
}
}
}
for (let value of collection) {
console.log(value)
}
// 1
// 2
// 3
встроенный итератор
итератор объекта коллекции
существуетES6Существует три типа объектов коллекций: массивы,Setсбор иMapКоллекции, все они имеют следующие три встроенных итератора:
-
entries: возвращает итератор, значениями которого являются несколько пар ключ-значение. -
values: возвращает итератор, значения которого являются значениями коллекции. -
keys: возвращает итератор, значением которого являются все имена ключей в коллекции.
entries()итератор:
const colors = ['red', 'green', 'blue']
const set = new Set([1, 2, 3])
const map = new Map([['name', 'AAA'], ['age', 23], ['address', '广东']])
for (let item of colors.entries()) {
console.log(item)
// [0, 'red']
// [1, 'green']
// [2, 'blue']
}
for (let item of set.entries()) {
console.log(item)
// [1, 1]
// [2, 2]
// [3, 3]
}
for (let item of map.entries()) {
console.log(item)
// ['name', 'AAA']
// ['age', 23]
// ['address', '广东']
}
valuesитератор:
const colors = ['red', 'green', 'blue']
const set = new Set([1, 2, 3])
const map = new Map([['name', 'AAA'], ['age', 23], ['address', '广东']])
for (let item of colors.values()) {
console.log(item)
// red
// green
// blue
}
for (let item of set.values()) {
console.log(item)
// 1
// 2
// 3
}
for (let item of map.values()) {
console.log(item)
// AAA
// 23
// 广东
}
keysитератор:
const colors = ['red', 'green', 'blue']
const set = new Set([1, 2, 3])
const map = new Map([['name', 'AAA'], ['age', 23], ['address', '广东']])
for (let item of colors.keys()) {
console.log(item)
// 0
// 1
// 2
}
for (let item of set.keys()) {
console.log(item)
// 1
// 2
// 3
}
for (let item of map.keys()) {
console.log(item)
// name
// age
// address
}
Итераторы по умолчанию для разных типов коллекций: у каждого типа коллекции есть итератор по умолчанию, вfor-ofВ цикле используется итератор по умолчанию, если не указано явное указание:
- массив и
SetКоллекция: итератор по умолчаниюvalues. -
MapКоллекция: по умолчаниюentries.
const colors = ['red', 'green', 'blue']
const set = new Set([1, 2, 3])
const map = new Map([['name', 'AAA'], ['age', 23], ['address', '广东']])
for (let item of colors) {
console.log(item)
// red
// green
// blue
}
for (let item of set) {
console.log(item)
// 1
// 2
// 3
}
for (let item of map) {
console.log(item)
// ['name', 'AAA']
// ['age', 23]
// ['address', '广东']
}
Деструктуризация и циклы for-of: если вы хотитеfor-ofИспользование синтаксиса деструктурирования в цикле упрощает процесс кодирования:
const map = new Map([['name', 'AAA'], ['age', 23], ['address', '广东']])
for (let [key, value] of map.entries()) {
console.log(key, value)
// name AAA
// age 23
// address 广东
}
итератор строк
посколькуES6С момента своего выпуска,JavaScriptСтроки медленно ведут себя как массивы:
let message = 'Hello'
for(let i = 0, len = message.length; i < len; i++) {
console.log(message[i])
// H
// e
// l
// l
// o
}
Итератор списка узлов
DOMодин из стандартныхNodeListТип, представляющий коллекцию всех элементов документа страницы.ES6Для него добавлен итератор по умолчанию, который ведет себя так же, как итератор по умолчанию для массивов:
let divs = document.getElementsByTagName('div')
for (let div of divs) {
console.log(div)
}
Оператор распространения и итерации без массива
Из предыдущих знаний мы уже знаем, что можем использовать оператор спреда для преобразованияSetКоллекция преобразуется в массив следующим образом:
let set = new Set([1, 2, 3, 4])
let array = [...set]
console.log(array) // [1, 2, 3, 4]
анализ кода: в нашем...В процессе распространения оператор работает наSetИтерация по умолчанию для коллекций (values), считывает все значения из итератора и вставляет их в массив в том порядке, в котором они возвращаются.
const map = new Map([['name', 'AAA'], ['age', 23], ['address', '广东']])
const array = [...map]
console.log(array) // [['name', 'AAA'], ['age', 23], ['address', '广东']]
анализ кода: в нашем использовании...В процессе распространения оператор работает наMapИтерация по умолчанию для коллекций (entries), прочитать несколько наборов пар ключ-значение из итератора и вставить их в массив по очереди.
const arr1 = ['red', 'green', 'blue']
const arr2 = ['yellow', 'white', 'black']
const array = [...arr1, ...arr2]
console.log(array) // ['red', 'green', 'blue', 'yellow', 'white', 'black']
анализ кода:В использовании...В процессе расширения оператора тот жеSetКоллекции используют свои итераторы по умолчанию (values), а затем вставьте их в массив в том порядке, в котором они возвращаются.
Расширенные функции итератора
Передать параметры итератору
если передано итераторуnext()Метод передает параметр, значение этого параметра заменит предыдущее внутри генератораyieldВозвращаемое значение оператора.
function * createIterator () {
let first = yield 1
let second = yield first + 2
yield second + 3
}
let it = createIterator()
console.log(it.next(11)) // {done: false, value: 1}
console.log(it.next(4)) // {done: false, value: 6}
console.log(it.next(5)) // {done: false, value: 8}
console.log(it.next()) // {done: true, value: undefined}
Анализ кода: помимо первого итератора мы можем быстро вычислить результат нескольких других итераторов, но почему первыйnext()Как насчет недопустимых параметров метода? Это потому, чтоnext()Параметр метода заключается в замене предыдущегоyieldвозвращаемое значение, а при первом вызовеnext()метод не будет выполнять никакихyieldзаявление, тем самым давая первый вызовnext()Метод передает параметры, любое переданное значение будет отброшено.
выдать ошибку в итераторе
В дополнение к передаче данных итератору также можно передать ему условия ошибки и заставить его выдавать ошибку при возобновлении выполнения.
function * createIterator () {
let first = yield 1
let second = yield first + 2
// 不会被执行
yield second + 3
}
let it = createIterator()
console.log(it.next()) // {done: false, value: 1}
console.log(it.next(4)) // {done: false, value: 6}
console.log(it.throw(new Error('break'))) // 抛出错误
Как мы видели выше, мы хотим передать объект ошибки внутри генератора, и ошибка будет выброшена, когда итератор возобновит выполнение, мы можем использоватьtry-catchзаявление, чтобы поймать этот вид ошибки:
function * createIterator () {
let first = yield 1
let second
try {
second = yield first + 2
} catch(ex) {
second = 6
}
yield second + 3
}
let it = createIterator()
console.log(it.next()) // {done: false, value: 1}
console.log(it.next(4)) // {done: false, value: 6}
console.log(it.throw(new Error('break'))) // {done: false, value: 9}
console.log(it.next()) // {done: true, value: undefined}
оператор возврата генератора
Поскольку генераторы также являются функциями, вы можете передатьreturnоператор завершает выполнение функции досрочно, в последний разnext()Вызов метода, вы можете указать для него возвращаемое значение.
function * createIterator () {
yield 1
return 2
// 不会被执行
yield 3
yield 4
}
let it = createIterator()
console.log(it.next()) // {done: false, value: 1}
console.log(it.next()) // {done: false, value: 2}
console.log(it.next()) // {done: true, value: undefined}
Анализ кода: в генератореreturnОператор указывает, что все операции завершены, и значение свойстваdoneбудет установлен наtrue, если также указано значение ответа, свойствоvalueбудет установлено это значение, иreturnпосле заявленияyieldвыполняться не будет.
展开运算符和for-of循环会直接忽略通过return语句指定的任何返回值,因为只要done被设置为true,就立即停止读取其他的值。
const obj = {
items: [1, 2, 3, 4, 5],
*[Symbol.iterator] () {
for (let i = 0, len = this.items.length; i < len; i++) {
if (i === 3) {
return 300
} else {
yield this.items[i]
}
}
}
}
for (let value of obj) {
console.log(value)
// 1
// 2
// 3
}
console.log([...obj]) // [1, 2, 3]
Генератор делегатов
Мы можем объединить два итератора в один, чтобы мы могли создать генератор и датьyieldОператор добавляет звездочку, чтобы делегировать процесс генерации данных другим итераторам.
function * createColorIterator () {
yield ['red', 'green', 'blue']
}
function * createNumberIterator () {
yield [1, 2, 3, 4]
}
function * createCombineIterator () {
yield * createColorIterator();
yield * createNumberIterator();
}
let it = createCombineIterator()
console.log(it.next().value) // ['red', 'green', 'blue']
console.log(it.next().value) // [1, 2, 3, 4]
console.log(it.next().value) // undefined
Если вы считаете, что это хорошо написано, пожалуйста, дайтеstar, если вы хотите прочитать все примечания в верхней и нижней частях, нажмитеЧитать статью полностью
Прочтите книгу «Углубленное понимание ES6» и систематизируйте заметки (часть 1)
Прочтите книгу «Глубокое понимание ES6» и систематизируйте заметки (ниже)