В этой статье вы найдете необходимые знания о JavaScript. В основном она знакомит с интерфейсным итератором и генератором JavaScript. Надеюсь, она будет полезна.
Вход в курс повышения квалификации по интерфейсу (vue): введите обучение
Итератор предоставляет унифицированный механизм интерфейса, обеспечивающий унифицированный механизм доступа к различным структурам данных.
Определение Iterator заключается в предоставлении объекту метода next(). При каждом вызове next() будет возвращен объект результата. Объект результата имеет два атрибута: значение представляет текущее значение, а значение Done указывает, завершен ли обход. .
функция makeIterator(Array){ пусть индекс = 0; возвращаться { следующий: функция(){ возвращаться ( Массив.длина > индекс? {значение: Массив[индекс++]}: {готово: правда} ) } } } пусть итератор = makeIterator(['1','2']) console.log(iterator.next()); // {значение: '1'} console.log(iterator.next()); // {значение: '2'} console.log(iterator.next()); // {готово: правда}
Роль Итератора:
Обеспечить единый и простой интерфейс доступа к различным структурам данных;
Позволяет членам структуры данных располагаться в определенном порядке;
для потребления для...из
ES6 предоставляет оператор for для обхода объекта итератора. Мы будем использовать оператор for для обхода созданного выше итератора:
пусть итератор = makeIterator(['1','2']) for (пусть значение итератора) { console.log(значение); } // итератор не является итеративным
В результате появляется ошибка, говорящая, что итератор не является итеративным. Почему это так? ES6 предусматривает, что интерфейс Iterator по умолчанию развертывается в свойстве Symbol.iterator структуры данных. Если структура данных имеет свойство Symbol.iterator, структуру данных можно обойти.
Мы преобразуем пользовательский makeIterator следующим образом:
const MakeIterator = (Array) => ({ [Символ.итератор](){ пусть индекс = 0; возвращаться { следующий(){ пусть длина = Array.length; если (индекс < длина) { вернуть {значение: Массив[индекс++]} }еще{ вернуть {готово: правда} } } } } }) for(пусть значение MakeIterator([1,2])){ console.log(значение) } // 1 // 2
Мы добавляем метод возврата в MakeIterator. Если цикл for...of завершается раньше (обычно из-за ошибки или оператора Break), метод return() будет вызван для завершения обхода.
На основе этой функции, если объекту необходимо очистить или освободить ресурсы перед завершением обхода, мы можем развернуть метод return() и включить закрытие файла в случае сбоя чтения файла.
const MakeIterator = (Array) => ({ [Символ.итератор](){ пусть индекс = 0; возвращаться { следующий(){ пусть длина = Array.length; если (индекс < длина) { вернуть {значение: Массив[индекс++]} }еще{ вернуть {готово: правда} } }, возвращаться(){ вернуть {готово: правда} } } } }) for(пусть значение MakeIterator([1, 2, 3])){ console.log(значение) // 1 // Метод 1 перерыв; // Метод 2 // выдаем новую ошибку('error'); }
множество
Набор
Карта
Объекты, подобные массивам, такие как объекты аргументов, объекты DOM NodeList, объекты typedArray.
// функция объекта аргументов sum(){ for(пусть значение аргументов){ console.log(значение) } } сумма(1,2) // 1 // 2 // объект typedArray let typeArry = new Int8Array(2); типАрри[0] = 1; типАрри[1] = 2; for(пусть значение typeArry){ console.log(значение) } // 1 // 2
Объект-генератор
функция*ген(){ выход 1; выход 2; } for(пусть значение gen()){ console.log(значение) }
Нить
Вопрос: Почему у Object нет встроенного итератора?
О: Причина, по которой Object не развертывает интерфейс Iterator по умолчанию, заключается в том, что неизвестно, какое свойство объекта проверяется первым, а какое свойство проверяется позже.
По сути, обходчик представляет собой линейный процесс. Для любой нелинейной структуры данных развертывание интерфейса обходчика эквивалентно развертыванию линейного преобразования.
Однако, строго говоря, интерфейс обходчика развертывания объекта не является необходимым, поскольку на данный момент объект фактически используется как структура Map. В ES5 нет структуры Map, но ES6 предоставляет ее изначально.
Деструктуризация задания
let set = new Set().add('a').add('b').add('c'); let [x,y] = set // x='a';
оператор распространения
вар стр = 'привет'; [...str] // ['h','e','l','l','o']
Оператор распространения вызывает интерфейс Итератора, поэтому Object не развертывает интерфейс Итератора, так почему же можно использовать оператор...?
Причина: существует два типа операторов распространения.
Один используется в случае параметров функции и расширения массива. В этом случае объект должен быть итерируемым (итерируемым).
Другой предназначен для расширения объекта, то есть формы {...obj}. В этом случае объект должен быть перечислимым (перечисляемым).
пусть объект1 = { имя: 'Цяньсюнь' } пусть объект2 = { возраст: 3 } // Объект массива является перечислимым let obj = {...obj1, ...obj2} console.log(obj) //{имя: 'qianxun', возраст: 3} // Обычные объекты по умолчанию не повторяются let obj = [...obj1, ...obj2] console.log(obj) // объект не повторяется
функция forOf(obj, cb){ пусть iteratorValue = obj[Symbol.iterator](); пусть результат = iteratorValue.next() пока(!result.done){ cb(результат.значение) результат = итераторЗначение.следующий() } } forOf([1,2,3], (значение)=>{ console.log(значение) }) // 1 // 2 // 3
Концептуально
Функция генератора — это решение асинхронного программирования, предоставляемое ES6. Функция Генератор — это конечный автомат, который инкапсулирует несколько внутренних состояний;
Функция Generator также является функцией генерации объекта-переходчика, которая возвращает объект-переходчик после выполнения.
формальный
1. Между ключевым словом функции и именем функции стоит звездочка;
2. Выражения доходности используются внутри тела функции для определения различных внутренних состояний.
функция* simpleGenerator(){ выход 1; выход 2; } простойГенератор()
Как указано выше, мы создали простой генератор и исследовали его, задав два вопроса:
Что происходит после запуска функции генератора?
Что делает выражение доходности в функции?
функция* simpleGenerator(){ console.log('Привет, мир'); выход 1; выход 2; } let генератор = simpleGenerator() // simpleGenerator {<suspended}}; console.log(generator.next()) // Привет, мир // {значение: 1, выполнено: ложь} console.log(generator.next()) // {значение: 2, выполнено: ложь}
Функция генератора-генератора возвращает объект-генератор после запуска, в то время как обычная функция будет напрямую выполнять код внутри функции каждый раз, когда вызывается следующий метод объекта-генератора, функция будет выполняться до тех пор, пока следующее ключевое слово доходности не прекратит выполнение, и объект {value: Value, Done: Boolean}.
Само выражение доходности не имеет возвращаемого значения или всегда возвращает неопределенное значение. Следующий метод может принимать один параметр, который будет рассматриваться как возвращаемое значение предыдущего выражения доходности. Через параметры следующего метода можно вводить разные значения снаружи внутрь на разных этапах функции Генератора для настройки поведения функции. Поскольку параметры следующего метода представляют собой возвращаемое значение предыдущего выражения доходности, передача параметров недействительна при первом использовании следующего метода.
функция сумма (х) { возвращаемая функция(у){ вернуть х + у; } } console.log(сумма(1)(2)) // Используем следующий параметр для перезаписи function* sum(x){ пусть y = доходность x; пока (правда) { у = доходность х + у; } } пусть gen = сумма (2) console.log(gen.next()) // 2 console.log(gen.next(1)) // 3 console.log(gen.next(2)) // 4
Роль выражения доходности: определение внутреннего состояния и приостановка выполнения. Разница между выражением доходности и оператором возврата.
Выражение доходности означает, что функция приостанавливает выполнение и в следующий раз продолжает выполняться в обратном направлении с этой позиции, в то время как оператор return не имеет функции памяти позиции.
В функции может быть выполнен только один оператор return, но может быть выполнено несколько выражений доходности.
Любая функция может использовать оператор return. Выражение доходности можно использовать только в функции-генераторе. При его использовании в другом месте будет выдано сообщение об ошибке.
Если в операции участвует выражение доходности, поместите его в круглые скобки; если оно используется в качестве параметра функции или размещено в правой части выражения присваивания, скобки можно не включать.
функция *gen () { console.log('привет' + выход) × console.log('привет' + (выход)) √ console.log('привет' + выход 1) × console.log('привет' + (выход 1)) √ foo(выход 1) √ константный параметр = выход 2 √ }
Основываясь на том факте, что функция-генератор Generator может поддерживать несколько выходов, мы можем реализовать сценарий, в котором функция имеет несколько возвращаемых значений:
функция* gen(num1, num2){ выход номер1 + номер2; выход номер1 - номер2; } пусть res = gen(2, 1); console.log(res.next()) // {значение: 3, выполнено: ложь} console.log(res.next()) // {значение: 1, выполнено: ложь}
Поскольку функция Generator является функцией генерации итератора, Generator можно назначить свойству Symbol.iterator объекта, чтобы объект имел интерфейс Iterator. Код реализации генератора более краток.
пусть объект = { имя: «Цяньсюнь», возраст: 3, [Символ.итератор]: функция(){ пусть это = это; пусть ключи = Object.keys(это) пусть индекс = 0; возвращаться { следующий: функция(){ вернуть индекс <keys.length? {значение: that[keys[index++]], Done: false}: {значение: неопределенное, выполнено: правда} } } } } for(пусть значение obj){ console.log(значение) }
Генератор:
пусть объект = { имя: «Цяньсюнь», возраст: 3, [Symbol.iterator]: функция* (){ пусть ключи = Object.keys(это) for(let i=0; i<keys.length; i++){ дать это [ключи [i]]; } } } for(пусть значение obj){ console.log(значение) }
Метод
return()
может вернуть заданное значение и завершить функцию генератора обхода.
функция*ген() { выход 1; выход 2; выход 3; } вар г = ген (); g.next() // { значение: 1, выполнено: ложь } // Если при вызове метода return() параметры не указаны, атрибут value возвращаемого значения не определен g.return('foo') // { значение: "foo", сделано: true } g.next() // { значение: неопределенное, выполненное: true }
Если внутри функции Generator есть блок кода try...finally
и блок кода try
выполняется, то метод return()
приведет к немедленному вводу блока finally
. После выполнения вся функция завершится.
функции* числа () { выход 1; пытаться { выход 2; выход 3; } окончательно { выход 4; доходность 5; } выход 6; } вар г = числа (); g.next() // { значение: 1, выполнено: ложь } g.next() // { значение: 2, выполнено: ложь } g.return(7) // { значение: 4, выполнено: ложь } g.next() // { значение: 5, выполнено: ложь } g.next() // { значение: 7, выполнено: true }
Если вы хотите вызвать другую функцию генератора внутри функции генератора. Нам нужно вручную завершить обход внутри тела функции первого. Если вызов функции вложен на нескольких уровнях, написание будет громоздким и трудным для чтения. В качестве решения ES6 предоставляет выражения yield*.
Делегировать другим генераторам
функция* g1() { выход 2; выход 3; } функция* g2() { выход 1; выход* g1(); выход 4; } константный итератор = g2(); console.log(iterator.next()); // { значение: 1, выполнено: ложь } console.log(iterator.next()); // { значение: 2, выполнено: ложь } console.log(iterator.next()); // { значение: 3, выполнено: ложь } console.log(iterator.next()); // { значение: 4, выполнено: ложь } console.log(iterator.next()); // { значение: неопределенное, выполнено: true }
Делегировать другим итерируемым объектам
функция*ген(){ доходность* [1,2,3] } console.log(gen().next()) // {значение: 1, выполнено: false}
Функция Generator возвращает обходчик. ES6 предусматривает, что этот обходчик является экземпляром функции Generator и наследует методы объекта Generator.prototype, но не может получить его свойства, поскольку в данный момент это глобальный объект, а не экземпляр. объект.
функция*ген(){ это.а = 1 } gen.prototype.say = функция(){ console.log('привет') } пусть объект = gen() console.log(obj instanceof gen) // true obj.say() // привет объект.следующий() console.log(obj.a) //не определено
Если вы хотите получить доступ к свойствам экземпляра, например, к конструктору, вы можете изменить это, чтобы привязать его к Generator.prototype.
функция*ген(){ это.а = 1 } gen.prototype.say = функция(){ console.log('привет') } пусть obj = gen.call(gen.prototype) console.log(obj instanceof gen) // true obj.say() // привет объект.следующий() console.log(obj.a) //1
функция* StateMachine(состояние){ пусть переход; пока (правда) { if(переход === "ИНКРЕМЕНТ"){ состояние++; }else if(transition === "DECREMENT"){ состояние--; } переход = выходное состояние; } } константный итератор = StateMachine(0); console.log(iterator.next()); // 0 console.log(iterator.next('ИНКРЕМЕНТ')); // 1 console.log(iterator.next('DECREMENT')); // 0