Рубрики
Без рубрики

Как не бояться веселых частей JavaScript

Часть 2 нашей серии обсуждает протоколы итерации, для петлей, а функция генератора является частью 2 мастерства JavaScript – и, вероятно, самые захватывающие части языка. (До тех пор, пока часть 3 не выходит, в любом случае;)) Часть 1 покрыла основы языка, и здесь мы будем покрывать итерационные протокола (ы), их использование

Автор оригинала: FreeCodeCamp Community Member.

Часть 2 нашей серии обсуждает протоколы итерации, для петлей и функций генератора

Это часть 2 мастерства JavaScript – и, вероятно, самые захватывающие части языка. (До тех пор, пока часть 3 приходит, во всяком случае;))

Часть 1 покрыла языковые основы И здесь мы будем покрывать итерационные протокола (ы), их использование для петлей и функции генератора.

Почему генератор функционирует в смеси? Если вы думаете, что это случайное дополнение, читайте! Генераторы связаны с итерацией!

Для петлей

Ну, вы знаете базовый для цикла, верно?

для (пусть; i

Вы бы использовали это для доступа к элементам в массиве.

Вы бы использовали что-то подобное для доступа к свойствам/значениям объекта:

для (пусть; i

И снова, что-то подобное для карта , Установить И любой другой пользовательский объект, который вы определяете. Когда вы хотите только значения/свойства, написание этого цикла, может привести к ошибкам. Вы можете использовать неверное свойство длины, вы можете сделать одно ошибки, или вы можете подумать Object.Keys (OBJ). Длина просто просто уродливая (я делаю).

Поскольку должен быть один лучший способ сделать вещи, здесь у нас есть для ... из и для ... в Петли! … Один лучший, верно?

Ну да. Они оба являются петлями, чтобы повторить что-то, но вот где сходство заканчивается, как мы увидим ниже.

Для … цикла

Давайте начнем с попыток повторить ценности в объекте.

Для доступа к элементам в массиве: для (пусть ценность ARR)

Для доступа к значению объекта: для (пусть var of tobile.values (obj))

Красиво, не так ли? Это порождает вопрос, однако, почему нет для (пусть var obj) Просто работать?

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

Добро пожаловать в мир Итерация протоколы.

Во-первых, краткое замечание о протоколах.

Если вы имели дело с Ооп Раньше, тогда вы, вероятно, знаете, какой интерфейс: это описание действий, которые можно сделать объект, как договор. Если вы хотите сделать Х , вам нужно иметь функцию, определенную в договоре, который делает X. Например, dox (a, b, c) который принимает параметры A, B, C. Таким же образом протоколы являются интерфейсами в JavaScript.

У нас есть 2 протокола итерации в JavaScript:

Передача протокола

Этот протокол позволяет объектам JS определить их итерационное поведение. Это включает в себя объект, который будет передан. Это также определяет, что именно итерации. Интерфейс требует метода [Symboly.iterator) где-то до цепь прототипа.

Итераторский протокол

Этот протокол определяет, как наш потенциальный протокол должен возвращать итоцируемые значения. Эх? Примером сделает этот яснее.

То, как мне нравится видеть, протокол итератора определяет интерфейс класса для итератора. (Если вы снова посмотрите на имя, это казалось бы довольно очевидным, да? Итератор Интерфейс. Смотри, я могу js сейчас.)

Возвращаясь к нашей дорогой документации:

Итак, наш интерфейс итератор полностью определяется существованием Далее () функция на объекте.

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

const iteratorObject = {
 next() {
     const value = Math.random();
     if ( value < this.threshold ) {
         return { done: false, value}; 
     }
     return { done: true};
 },
 [Symbol.iterator]: function() {
     return this;
 },
 threshold: 0.7
}

Красота лежит в [Символ .-ТРЕТИКА] часть итератора. Определив это, мы позволяем нашему итератору подвергаться воздействию различных функций и синтаксисов, которые нуждается в итеративный протокол, а не только протокол итератор. Что вы можете сделать с этим?

Помните оператор распространения? – Это также принимает итеративный протокол!

>[...iteratorObject] 
[0.03085962239970308, 0.20649861146804716]

И, конечно же, работает с для ... из , где началась эта история.

>for (let val of iteratorObject) {
    console.log(val);
}
0.6234680935767514
0.525812241023621

Под капотом мы можем теперь понять, что происходит: все эти методы используют [Символ .-ТРЕТИКА] генерировать итератор и итерацию над тем, что использует Следующий !

>const iter = iteratorObject[Symbol.iterator]()
undefined
>iter.next();
{done: false, value: 0.04474940944875905}
>iter.next();
{done: true}

Обязательно облегчает вещи, когда вам не нужно делать это самостоятельно. Там один бит, мы не трогали, что идет рука об руку с для ... из петли, которые есть: для ... в Отказ Какая разница? Давайте погрузимся, начиная с нашего примера!

Для … в петлях

>for (const val in iteratorObject) {
    console.log(val);
}
next
threshold

На простом взгляде разница кажется очевидной: для ... в Получает свойства, пока для ... из Получает значения! Почему [Symboly.iterator] отсутствует тогда? Ну, есть 2 причины.

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

> Object.getOwnPropertyDescriptors(iteratorObject)
{ next:
   { value: [Function: next],
     writable: true,
     enumerable: true,
     configurable: true },
  threshold:
   { value: 0.7,
     writable: true,
     enumerable: true,
     configurable: true },
  [Symbol(Symbol.iterator)]:
   { value: [Function: [Symbol.iterator]],
     writable: true,
     enumerable: true,
     configurable: true } }

для ... в Цветные петли по свойствам, перечислимый дескриптор которого устанавливается на true, а также не символьные свойства. Это объясняет это, верно? Просто для подтверждения, вы можете добавить новое свойство на объект, с перечисляемым настроен на false, и он не будет отображаться в для ... в петля.

Object.defineProperty(iteratorObject, "newHiddenProperty", {
    enumerable: false,
    value: "hidden",
})

Конечно, это все еще не там. Object.keys () использует той же методологию.

>for(const val in iteratorObject) {
    console.log(val);
}
next
threshold

Возвращаясь к вопросу, которое заставило нас пойти вниз по этой кроличной дыре – почему нет для (пусть Val Obj) Просто работать? Теперь вы знаете, верно? Поскольку не существует неотъемлемого протокола по прототипу объекта!

Почему нет? Простой ответ – это выбор языкового дизайна. Почему они выбрали это? Потому что многие объекты наследуют от базового объекта. Наличие итогеруемого протокола на базовом объекте будет означать, что все эти объекты являются утеряемыми. Например: ваши объекты даты становятся итеративными, что не имеет никакого смысла.

Foreache Lop.

Это приводит нас к последнему виду для циклов: Foreach Coop. Я видел, как люди запутались, почему нет foreach Работайте везде (как на объектах), и я отвечу этот вопрос здесь.

Простой ответ – Array.prototype.foreach () Отказ

foreach Цикл определяется только для массивов! Итак, вы можете использовать их только с массивами. Теперь foreach не заботится, откуда приходит этот массив. Это может быть простой родной массив или массив, генерируемый объектами, как object.keys ().

Чтобы закончить раздел петель, одна общая банка.

При использовании объектов в JS в качестве карт (или словари, HASHMAP) вы можете столкнуться с проблемами, когда какой-то ключ совпадает с свойством цепочки прототипа.

Рассмотрим этот пример:

У вас есть объект с определенными ключами, которые вы хотите включить.

const baseObject = {
  a: 1,
  b: 2,
  someProperty: function() {
    return 4;
  }
}


const myObjectMap = Object.create(baseObject);

myObjectMap.c = 3; // key set in map for some reason.

for(let val in myObjectMap) { // this iterates up the chain!
  console.log(val);
}

> c
 a
 b
 someProperty

Вы, наверное, просто хотел увидеть C ключ, который вы установили. Вы можете исправить это через:

for (let val in myObjectMap) {
  if (myObjectMap.hasOwnProperty(val)) {
    console.log(val);
  }
}

> c

Таким образом, два правила, чтобы избежать этой проблемы:

  1. Всегда используйте HasownProperty () Чтобы проверить, вы ищете ключ, существуют в объекте (а не в цепочке проторовки)
  2. Никогда не используйте HasownProperty как ключ в ваших словарях/карты.

Если вы переопределили HasownProperty , еще есть способ использовать его, поскольку это метод прототипа объекта.

myObjectMap.hasOwnProperty = 4;

for(let val in myObjectMap) {
    if (myObjectMap.hasOwnProperty(val)) {
        console.log(val);
    }
}
> Uncaught TypeError: myObjectMap.hasOwnProperty is not a function
    at :4:21

// instead, we can do: 
for(let val in myObjectMap) {
    if (Object.prototype.hasOwnProperty.call(myObjectMap, val)) {
        console.log(val);
    }
}

> c
  hasOwnProperty

Помните Позвоните и Применить от последней части? Это один удивительный способ использовать их.

Функции генератора

Функции генератора Разрешить ввод на запросы и выйти из функции. Точки входа и выхода исправлены. Это как многократная входная виза.

Они очень мощные инструменты, чтобы получить сложные вещи.

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

Почему бы не просто проиграть на массиве ценностей? Ну, генераторы экономиют пространство. Не существует массива, чтобы начать с – просто вычисление (или ввод/вывод), необходимо получить следующий элемент из «массива».

Давайте погрузимся в механику этого.

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

Это доходность Выражение также указывает значение, которое нужно возвращено.

Давайте сделаем эту концепцию бетон с примером. Давайте сделаем Tweet Trial из части 1.

function * generateTweets(userID, numberOfTweets) {
    for(let i=0; i< numberOfTweets; i++) {
        const tweet = randomTweetGenerator(); // assume this gives you a string of words < 280 characters.
        yield { tweet, userID, tweetID: i};
    }
}

const tweetList = generateTweets('neilkakkar', 3);
for( let tweet of tweetList) {
	  console.log(tweet);
}

> {tweet: "hi", userID: "neilkakkar", tweetID: 0}
  {tweet: "how's it going?", userID: "neilkakkar", tweetID: 1}
  {tweet: "I'm automagic", userID: "neilkakkar", tweetID: 2}


console.log(tweetList.next());
>    {value: undefined, done: true}

Хорошо, здесь многое здесь. Давайте сломаемся.

Во-первых, у нас есть генератор функции, который генерирует твиты на основе UserID и количество твитов для генерации. Эта функция вернет бы объект итератора. Таким образом, вот что TweetList является.

> tweetList
generateTweets {}
    __proto__: Generator
    [[GeneratorLocation]]: VM2668:1
    [[GeneratorStatus]]: "suspended"
    [[GeneratorFunction]]: ƒ * generateTweets(userID, numberOfTweets)
    [[GeneratorReceiver]]: Window
    [[Scopes]]: Scopes[3]

Подвески означает, что генератор еще не закрыт/закончен. Итак, существуют ценности, которые он может обеспечить. Мы можем получить доступ к этому через TweetList.next () – Что даст нам объект с двумя ключами, ценность и сделано Отказ

На оборотной стороне для ... из Петли понять протокол итерации, чтобы они могли соблюдать весь генератор самостоятельно!

Это именно поэтому мы можем сделать для ... из на TweetList и получить наши твиты.

На данный момент генератор закончен. для ... из петля потребляет все значения.

У нас здесь

> tweetList
generateTweets {}
    __proto__: Generator
    [[GeneratorLocation]]: VM2668:1
    [[GeneratorStatus]]: "closed"
    [[GeneratorFunction]]: ƒ * generateTweets(userID, numberOfTweets)
    [[GeneratorReceiver]]: Window

Таким образом, когда мы регистрируем следующее значение в следующей строке, мы получаем сделано: правда Как мы ожидаем – и никаких ценностей.

Это все для примера.

Но эта история не заканчивается здесь. Вы можете получить генераторы, а также генераторы! Вы делаете это через Урожай * Отказ

function * generateTweetsForSomeUsers(users, numberOfTweets) {
    for(let user of users) {
        yield * generateTweets(user, numberOfTweets)
    }
}

Генераторы могут также Возвращение вместо доходность Отказ Это приводит к тому, что генератор закончен.

Ну, это поступило достаточно долго, я думаю, что спаснул другие прохладные биты для следующих частей. Забавный факт? Мы вообще избавимся от петель. Добро пожаловать в мир карты, фильтр и уменьшить.

Читать больше сообщений моего блога на neilkakkar.com Отказ