Дополнение к знанию грамматики ES6 почти на 10 000 слов

внешний интерфейс ECMAScript 6

предисловие

ECMAScript 6.0 (сокращенно ES6), как стандарт языка JavaScript следующего поколения, был официально выпущен в июне 2015 г. Он был выпущен более 3 лет назад, однако из-за широкого диапазона синтаксиса, который он содержит, потребуется некоторое время. определенное количество времени, чтобы полностью переварить.Здесь я суммирую некоторые из ES6, а также точки знаний и сценарии использования нового синтаксиса после ES6, я надеюсь, что это будет вам полезно

Эта статья посвящена дополнению функций синтаксиса ES6 и не будет объяснять некоторый синтаксис уровня API, а больше посвящена изучению принципов, лежащих в его основе, и проблемам, которые решает ES6.

Если есть какая-либо ошибка, пожалуйста, укажите на нее, она будет исправлена ​​как можно скорее, и предложения по исправлению приветствуются.

Без лишних слов, давайте начнем путешествие по ES6~~~

let/const (обычно используется)

Let и const используются для объявления переменных и замены ключевого слова var в старом синтаксисе.В отличие от var, let/const создает область действия на уровне блока (как правило, новая область действия находится внутри фигурной скобки).

Здесь внешний console.log(x) не может получить let, объявленный в первых двух областях блока:

Он в основном используется в повседневной разработкеif/forключевые слова в сочетании с областью блока, созданной с помощью let/const,Стоит отметить, что цикл for, объявляющий переменные с помощью ключевого слова let/const, несколько отличается от объявления var.

Цикл for разделен на три части: первая часть содержит объявление переменной, вторая часть содержит условие выхода из цикла, а третья часть содержит выражение, которое должно выполняться в конце каждого цикла, то есть первая часть будет использоваться только в этом цикле for. Выполнить var i = 0 один раз, а следующие две части будут выполняться один раз в каждом цикле.

Используя цикл for, который объявляет переменную с помощью ключевого слова let/const, в дополнение к созданию области на уровне блока, let/const также привязывает ее к каждому циклу, гарантируя, что значение в конце предыдущего цикла будет переназначено.

Что это значит? Короче говоря, он будет объявлен один раз за цикл (по сравнению с циклом for, объявленным var, будет объявлен только один раз), вы можете понять цикл for в let/const следующим образом

Создайте область блока для каждого цикла:

Временная мертвая зона

Переменные, объявленные с помощью let/const, с самого начала образуют закрытую область видимости, эту переменную нельзя использовать до того, как переменная будет объявлена, эта особенность также должна компенсировать недостаток var (переменные, объявленные с помощью var, имеют продвижение переменной)

Проанализируйте принцип временной мертвой зоны,На самом деле, let/const также имеет эффект повышения, но отличие от var в том, что

  • var инициализируется при создании и назначается undefined

  • После того, как let/const войдет в область действия на уровне блока, он будет создан первым для продвижения, но не будет инициализирован до тех пор, пока не будет выполнен оператор объявления.Если переменной, объявленной с помощью let, не присвоено значение во время инициализации, по умолчанию она будет присвоена не определено, и константа должна быть назначена во время инициализации. А фрагмент кода между созданием и инициализацией образует временную мертвую зону

ЦитироватьблогАбзац, переведенный из стандарта ES6

Переменные, объявленные с помощью let/const, создаются при создании экземпляра лексической среды, которую они содержат, но доступ к ним возможен только после оценки LexicalBinding переменной.

Вернуться к нашему примеру, дайте заявителю здесь из-за использования имени переменной, код для выполнения, когда оператор IF будет сначала ввести стадию предварительного компиляции, за которым следует создание объема на уровне блока, лексической среды, переменных именных переменных ( Не инициализированные), затем введите сцену выполнения кода, только во время выполнения, чтобы отпустить имя имени была только переменная инициализирована, и назначение по умолчанию не определено, но из-за временного лидера в мертвете.ДоИспользуется переменная имени, поэтому сообщается об ошибке

В приведенном выше примере, поскольку var используется для объявления переменных, будет происходить продвижение переменных, которое также происходит на этапе предварительной компиляции, var будет продвигаться наверх текущей области действия функции, а значение по умолчанию не определено, если эти строки кода находятся в глобальной области, переменная имени будет напрямую переведена в глобальную область, а затем войдет в стадию выполнения для выполнения кода, имя присваивается «abc», и строка abc может быть успешно напечатана

эквивалентно этому

Временная мертвая зона на самом деле предназначена для предотвращения использования этой переменной ES5 до объявления переменной, потому что функция продвижения переменной var заставляет некоторых разработчиков, не знакомых с принципом var, думать, что переменная может быть использована в объявлении сначала , чтобы похоронить его. некоторые скрытые опасности

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

const

Используйте ключевое слово const для объявления константы. Константа означает переменную, которая не будет изменяться. Некоторые различия между const и let заключаются в следующем.

  1. Const объявленные переменными должны быть назначены время, в противном случае это будет ошибка, используйте те же переменные, объявленные Const Error будут изменены

  1. Константная переменная объявления не может быть изменена.Если объявление ссылочного типа, его адрес памяти не может быть изменен (это связано с характеристиками ссылочного типа JS. Если вам интересно, вы можете прочитать мой другой блог.Глубокое копирование объекта и мелкое копирование)

Некоторые люди могут задаться вопросом, почему в повседневной разработке нет явного объявления области действия на уровне блоков, но переменные, объявленные с помощью let/const, не становятся глобальными переменными.

На самом деле это особенность let/const.ES6 предусматривает, что они не относятся к свойствам глобальных переменных верхнего уровня.Здесь мы используем хром для отладки.

Можно видеть, что переменная x, объявленная с помощью let, находится в области, называемой script, а переменная, объявленная с помощью var, продвигается до объекта окна глобальной переменной из-за продвижения переменной, что позволяет нам уверенно использовать новый синтаксис, не беспокоясь о загрязняя глобальный объект окна

предложение

В повседневной разработке я предлагаю полностью использовать let/const, общие объявления переменных использовать ключевое слово let, а при объявлении некоторых элементов конфигурации (таких как адреса интерфейсов, зависимости npm, страницы по умолчанию пейджера и т. д.) переменные, которые не будут меняться) , вы можете использовать const, чтобы явно сообщить другим разработчикам проекта, что эта переменная не может быть изменена Дефекты ключевого слова var (поднятие переменной, загрязнение глобальных переменных и т. д.), чтобы можно было лучше использовать новый синтаксис

Стрелочные функции (обычно используемые)

ES6 позволяет использоватьстрела(=>) определить функцию

Стрелочные функции имеют следующие отличия от функций, созданных с помощью ключевого слова function.

  1. Стрелочные функции не имеют аргументов (предпочитают лучший синтаксис, вместо этого оператор rest)

  2. Стрелочные функции не имеют свойства прототипа и не могут использоваться в качестве конструкторов (не могут вызываться с ключевым словом new).

  3. У стрелочной функции нет собственного this, это лексическое значение, и оно ссылается на this контекста, то есть, когда вы пишете эту строку кода, this стрелочной функции уже привязано к this внешнего исполнения. контекст (лично я думаю, что это не означает, что он полностью статичен, потому что внешний контекст по-прежнему динамичен и может быть изменен с помощью call, apply и bind. Это просто показывает, что this стрелочной функции всегда равно this в верхнем контексте)

Так как setTimeout затолкнет анонимную callback-функцию в асинхронную очередь, а callback-функция глобальная, то есть в нестрогом режиме, то это будет указывать на window, и будет проблема потери переменной a, а если использовать стрелочные функции, в При написании было определено, что его this равно его контексту (вот контекст выполнения функции makeRequest, что эквивалентно привязке this в стрелочной функции к this в контексте выполнения функции makeRequest) потому что это функция makeRequest, вызываемая объектом контроллера, так что это указывает на переменную в объекте контроллера

Эта точка стрелочной функции не может быть изменена, даже если используется вызов, применение, привязка (это также подтверждает, почему ECMAScript предусматривает, что стрелочные функции не могут использоваться в качестве конструкторов, потому что это было определено и не может быть изменено)

предложение

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

ES5 Написать:

Стрелочные функции ES6:

Давайте посмотрим на другой пример

Стоит отметить, что функция makeRequest не может использовать стрелочные функции, потому что тогда она будет использовать this верхнего уровня, а верхний уровень является глобальным контекстом выполнения, и его значение this будет указывать на окно, поэтому переменная a не может быть найден и возвращает значение undefined

Более лаконично использовать стрелочные функции в итерации массива и опускать ключевое слово return.

Не используйте стрелочные функции в функциях, которые могут изменить эту точку, подобно методам в Vue, методам в вычисляемых функциях и функциях жизненного цикла. Vue привязывает this этих функций к экземпляру vm текущего компонента. Если вы используете стрелочные функции, это будет принудительно изменено это, потому что стрелочная функция имеет наивысший приоритет (больше нельзя использовать вызов, применение, привязку для изменения точки)

Прежде чем использовать стрелочные функции в качестве синтаксиса для ежедневной разработки, мое личное предложение состоит в том, чтобы понять, как стрелочные функции связывают это, а не просто опускать написание слов функция, в конце концов, это проблема, которую ECMAScript действительно надеется решить.

итератор итератор

Итератор — очень важная концепция в ES6, но многие люди мало что о ней знают, но он является основой реализации для других 4 общих функций ES6 (деструктурирующее присваивание, оператор остатка/расширения, генератор, цикл for of). Концепция итератора помогает понять принципы других четырех основных грамматик. Кроме того, новые структуры данных Map и Set в ES6 также используют ее, поэтому я помещу ее впереди.

Для итерируемой деструктуризации данных ES6 внутренне развертывает свойство [Symbol.iterator], которое представляет собой функцию, которая возвращает объект итератора (также называемый объектом итератора) после выполнения и генерирует объект итератора [Symbol.iterator]. Свойство называется Iterator. интерфейс, структуры данных с этим интерфейсом считаются итерируемыми

Метод Symbol.iterator (интерфейс итератора) в массиве развернут на прототипе массива по умолчанию:

Структуры данных по умолчанию для развертывания интерфейса итератора следующие: обратите внимание, что обычные объекты не имеют интерфейса итератора по умолчанию (вы можете сами создать интерфейс итератора, чтобы обычные объекты также могли быть итерированы)

  • Array
  • Map
  • Set
  • String
  • TypedArray (как массив)
  • объект аргументов функции
  • Объект NodeList

итератор итератор - это объект, у него есть метод next, поэтому его можно вызывать так

Следующий метод возвращает объект с двумя свойствами: value и done. Значение — это значение, возвращаемое после каждой итерации, а done указывает, следует ли повторять цикл. Вы можете видеть, что когда значение не определено, done равно true, чтобы указать на завершение цикла

разбираться

  • Итерируемые структуры данных будут иметь метод [Symbol.iterator].
  • [Symbol.iterator] Возвращает объект итератора после выполнения
  • объекты итератора имеют следующий метод
  • Однократное выполнение следующего метода (использование итератора) вернет объект со значением и готовыми свойствами.

заниматьБлог Сае ЮИтераторы, реализованные в ES5, могут иметь более глубокое понимание того, как генерируются и используются итераторы.

Назначение деструктурирования (обычно используется)

Присвоение деструктуризации может напрямую использовать атрибут объекта, не используя его в форме доступа к атрибуту.Я лично думаю, что принцип деструктуризации объекта заключается в том, чтобы найти то же имя атрибута, а затем присвоить значение имени атрибута исходного объекта к соответствующему атрибуту нового объекта.

Реальное объявление слева здесь на самом деле представляет собой две переменные titleOne и titleTwo, а затем, в соответствии с позициями двух переменных слева, оно будет искать значение, соответствующее title в title и test[0] в объекте на справа, и найдите строки abc и проверьте и назначьте их titleOne, titleTwo (если не найдено, он вернет undefined)

Принцип деструктуризации массива заключается в использовании итератора массива и присвоении значения атрибута value сгенерированного объекта соответствующей переменной.

Одним из применений деструктуризации массива является замена переменных местами, избегая необходимости объявлять значение временной переменной для сохранения значения перед

Переменные подкачки ES6:

предложение

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

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

Без деструктуризации объекта:

Используйте деструктурирование объекта:

Кроме того, вы можете деконструировать результаты ответа с помощью axios (по умолчанию axios поместит реальные результаты ответа в атрибут данных).

Остаточный/расширенный оператор (обычный)

Оператор rest/spread также является очень важным синтаксисом в ES6, используя 3 точки (...), за которыми следует структура данных с интерфейсом итератора.

спред оператор

Взяв в качестве примера массив, использование оператора спреда позволяет «расширить» массив, можно понять, что массив — это контейнер для хранения набора элементов, а оператор спреда можно использовать для дизассемблирования контейнера, чтобы осталась только коллекция элементов.Вы можете поместить эти коллекции элементов в другой массив

Оператор распространения может заменить метод concat для прототипов массивов в ES3.

Здесь arr1, arr2 расширяются оператором расширения, а затем эти элементы помещаются в новый массив, который более семантичен, чем метод concat.

оператор остатка

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

Доступ к объекту arguments функции — очень затратная операция, предыдущие arguments.callee и arguments.caller упразднены, рекомендуется не использовать объект arguments в среде, поддерживающей синтаксис ES6, а вместо этого использовать оставшийся оператор ( стрелочные функции не имеют аргументов, для доступа к набору параметров необходимо использовать оператор rest)

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

Здесь сначала будет потребляться итератор массива справа, ...arr будет потребляться все оставшиеся итераторы, а во втором примере ...arr будет напрямую потреблять все итераторы, в результате чего не будет итераторов для последнего использования, поэтому будет отчет ошибка, потому что это бессмысленная операция

Разница между оператором остатка и оператором спреда заключается в том, что оператор остатка собирает эти наборы и помещает их в массив справа, а оператор спреда разбивает массив справа на наборы элементов, которые являются противоположными.

Использование оператора распространения с объектами

Это синтаксис ES9.ES9 поддерживает использование операторов распространения в объектах.Я говорил ранее, что принцип операторов распространения для массивов состоит в том, чтобы потреблять все итераторы, но в объектах нет итераторов.Я лично думаю, что принцип реализации может быть другим, но его все равно можно понимать как отделение пары ключ-значение от объекта, его можно поместить в другой обычный объект

На самом деле, похоже на другую новую API ES6, объект.assign, они могут объединить объекты, но есть какой-то другой объект. Tassign Trigger STATTER функции целевого объекта, а оператор расширения объекта не делает это обсуждаться позже

предложение

Используйте оператор распространения, чтобы быстро преобразовать массив, подобный массиву, в реальный массив.

Объединить несколько массивов

каррирование функций

Сокращение свойства/метода объекта (обычно используется)

Сокращение свойства объекта

ES6 позволяет пропустить имя свойства, когда свойство и значение объекта одинаковы

должен быть в курсе

  • Имя свойства опущено вместо значения
  • значение должно быть переменной

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

В сочетании с присваиванием деструктурирования выше код здесь фактически объявит переменные x, y и z, потому что функция bar вернет объект с тремя свойствами x, y и z, а присваивание деструктурирования будет искать правильный сторона знака равенства Находятся свойства x, y, z выражения и присваиваются объявленным переменным x, y, z

сокращение метода

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

В Vue, поскольку методы написаны в объекте vm, вполне возможно использовать сокращение метода для записи функций.

for... цикла

for ... of — это новый метод обхода в ES6, который позволяет проходить структуру данных, содержащую интерфейс итератора, и возвращать значение каждого элемента Отличие от for ... in в ES3 заключается в следующем.

  1. for... of можно использовать только для итерируемых объектов, чтобы получить значение, возвращаемое итератором, for... in можно получить имена ключей всех объектов

  2. for... in будет проходить всю цепочку прототипов объекта, производительность очень низкая и не рекомендуется, а for... of только проходит текущий объект и не пересекает его цепочку прототипов

  3. Для обхода массива for...in вернет все перечисляемые свойства в массиве (включая перечисляемые свойства в цепочке прототипов), а for...of вернет только значение свойства, соответствующее индексу массива.

Принцип цикла for...of фактически использует интерфейс итератора, развернутый внутри итерируемого объекта.Если цикл for...of разложить на самый примитивный цикл for, внутренний механизм реализации можно понять таким образом

Можно видеть, что пока выполняется второе условие (iterator.next() существует, а res.done имеет значение true), цикл может продолжаться, и каждый раз, когда объект, сгенерированный методом next итератора, присваивается res, и затем присваивается значение res Атрибут присваивается переменной, объявленной в первом условии for ... of, а атрибут done of res контролирует, следует ли продолжать обход

Цикл for...of также поддерживает разрыв, продолжение, возврат (если вызывается в функции) и может использоваться с назначением деструктурирования объекта.

Массив arr возвращает объект ({a:1},{a:2},{a:3}) каждый раз, когда он использует цикл for...of, а затем деконструирует объект, находит значение атрибута a , и присваивает его obj.a, так что obj.a будет присвоено 1, 2, 3 в каждом раунде цикла соответственно

Обещания (обычно используется)

Как новая концепция, представленная в ES6, промисы изменили асинхронное программирование JS. Большинство асинхронных запросов в современном интерфейсе реализованы с использованием промисов. Выборка веб-API также основана на промисах. Нет необходимости кратко описывать предыдущее правило асинхронного программирования JS Функция обратного вызова, каковы недостатки функции обратного вызова и как Promise устраняет эти недостатки

Перезвоните

Как мы все знаем, JS является однопоточным, потому что если несколько потоков изменяют DOM, страница будет в беспорядке, поэтому он разработан как однопоточный язык, но браузер является многопоточным, что делает JS асинхронными операциями. одновременно, то есть таймеры, Запрос, мониторинг событий и т. д. В это время необходим набор механизмов обработки событий для определения порядка этих событий, а именно Event Loop (цикл событий). быть подробно объяснено здесь, просто нужно знать, что запрос, выдаваемый внешним интерфейсом, как правило, все они войдут в поток http-запроса браузера, когда ответ будет получен, он будет помещен в асинхронную очередь через обратный вызов Когда задача основного потока будет обработана, будет прочитана задача в асинхронной очереди и выполнен обратный вызов.

Во втором томе «JavaScript, которого вы не знаете» это введение.

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

Используя стороннюю библиотеку запросов, вы можете написать:

После получения ответа выполните следующий обратный вызов, чтобы напечатать строку, но если в этой сторонней библиотеке есть такая функция, как повтор тайм-аута, она может выполнить вашу функцию обратного вызова несколько раз.Если это платежная функция, вы обнаружите, что у вас есть вычитается Деньги могут быть более 1000 юаней -.-

Вторая известная проблема заключается в том, что вложение функций обратного вызова в функции обратного вызова может сильно усложнить поддержку кода, что часто называют «адом обратных вызовов».

Кроме того, используемая вами сторонняя библиотека ajax может не предоставлять некоторые неправильные обратные вызовы, а некоторые сообщения об ошибках неудачных запросов могут быть проглочены, а вы совершенно не подозреваете (nodejs предоставляет обратные вызовы в стиле err-first, то есть асинхронные). операции всегда является обработчиком обратного вызова ошибки, но вы все равно не можете гарантировать, что все библиотеки предоставляют функцию обратного вызова, которая будет выполняться при отправке ошибки)

Суммируйте некоторые недостатки функций обратного вызова.

  1. Множественное вложение, приводящее к аду обратного вызова

  2. Прыжки кода, а не то, как люди привыкли думать

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

  4. Сторонние библиотеки могут не обеспечивать обработку ошибок

  5. Непонятно, все ли колбэки вызываются асинхронно (можно вызывать ajax синхронно, весь поток будет заблокирован до получения ответа, и он попадет в состояние suspend, что крайне не рекомендуется)

xhr.open("GET","/try/ajax/ajax_info.txt",false); //通过设置第三个async为false可以同步调用ajax

Promise

Ввиду недостатков callback-функций в ES6 была введена новая концепция Promise.Promise — это функция-конструктор.Создайте экземпляр Promise с помощью нового ключевого слова и посмотрите, как Promise решает эти проблемы callback-функций.

Promise — это не производная версия функции обратного вызова, а две концепции, поэтому необходимо изменить предыдущую функцию обратного вызова на версию, поддерживающую Promise.Этот процесс становится «продвижением» или «обещанием», сторонней библиотекой запросов. обычно используемый современными фреймворками MVVM axios является типичным примером, а также есть bluebird, Q и т. д. в nodejs

  1. Множественное вложение, приводящее к аду обратного вызова

Promise вводит концепцию связанных вызовов при проектировании, каждый затем методТакже обещание, поэтому его можно вызвать в бесконечной цепочке

Со стрелочными функциями это, очевидно, намного элегантнее, чем предыдущее многоуровневое вложение функций обратного вызова.

  1. Прыжки кода, а не то, как люди привыкли думать

Promise обеспечивает синхронное мышление для написания кода.Вышеприведенный код должен сначала запросить порт 3000, получить ответ, затем запросить 3001, затем запросить 3002, а затем запросить 3003, и формат записи также соответствует человеческому мышлению, от первого до прошлой

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

Обещание само по себе является конечным автоматом со следующими 3 состояниями

  • в ожидании
  • выполнено (успех)
  • отклоненный

При отправке запроса без ответа он находится в состоянии pending, после получения ответа он резолвит (разрешит) текущий экземпляр промиса и превратит его в выполненный/отклоненный (в большинстве случаев он станет выполненным)

Когда в запросе возникает ошибка, будет выполнено reject, чтобы перевести экземпляр Promise в отклоненное состояние.

Состояние экземпляра Promise может быть изменено только с pending => выполнено или с pending => rejected, то есть при переходе экземпляра Promise из состояния pending оно не изменится (нет выполнено => отклонено или отклонено = > выполнено)

Экземпляр промиса должен активно вызывать метод then для получения значения из экземпляра промиса (при условии, что промис не находится в состоянии ожидания).Эта «активная» операция является ключом к решению этой проблемы, то есть третья- сторонняя библиотека изменяет только промис.Статус ответа и как поступать со значением ответа активно контролируется разработчиком.Здесь реализована инверсия управления, а управление исходной сторонней библиотекой осуществляется передан застройщику.

  1. Сторонние библиотеки могут не обеспечивать обработку ошибок

Метод then класса Promise принимает две функции: первая функция — это обратный вызов, выполняемый при разрешении экземпляра Promise, а вторая функция — обратный вызов, выполняемый при отклонении экземпляра Promise, который также активно вызывается разработчиком.

При использовании обещания отправить ошибку в асинхронном запросе, даже если ошибка не поймана, она не заблокирует код основного потока (точнее, асинхронные ошибки не будут блокировать код основного потока)

  1. Не уверен, что все обратные вызовы вызываются асинхронно

Promise предназначен для обеспечения того, чтобы все обратные вызовы обработки ответов вызывались асинхронно и не блокировали выполнение кода. Promise помещает обратный вызов метода then в очередь с именем MicroTask, чтобы гарантировать, что эти задачи обратного вызова являются синхронными задачами. После выполнения выполните его еще раз. Эта часть также является точкой знаний о цикле событий. Заинтересованные друзья могут изучить его подробно.

Что касается третьего вопроса, почему вы говорите, что «большинство случаев» перейдут в состояние выполнения после выполнения функции разрешения?Рассмотрите следующие ситуации.

(Здесь таймер используется для печати состояния этого экземпляра Promise в следующем цикле событий, иначе это будет состояние ожидания)

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

Здесь, поскольку передано обещание в состоянии отклонения, после того, как функция разрешения расширит это обещание, оно станет обещанием в состоянии отклонения, поэтому лучше понимать разрешение как разрешение.

эквивалентно этому

предложение

В повседневной разработке рекомендуется полностью использовать новый синтаксис промисов, поскольку асинхронное программирование в основном использует промисы.

Рекомендуется использовать async/await ES7 для дальнейшей оптимизации метода написания Promise.Асинхронная функция всегда возвращает промис, а await может реализовывать функцию ожидания.Async/await стал окончательным решением для асинхронного программирования, то есть , записывая асинхронный код в форме кода синхронизации, и может более элегантно реализовывать последовательное выполнение асинхронного кода и предоставлять более точную информацию об ошибке при возникновении асинхронной ошибки.

О Promise можно сказать гораздо больше, в том числе о его статических методах all, race, resolve, reject, порядке выполнения Promise, Promise вложенных Promise, обработке объектов и т.д. использовать его. Однако многие разработчики понимают эти API только в повседневном использовании, но не знают, как реализована внутренняя реализация Promise.При столкновении со сложным асинхронным кодом невозможно начать.Настоятельно рекомендуется понять спецификацию Promise A+ и реализовать Promise на сами.

Модуль ES6 (обычно используется)

До появления модуля ES6 модуляризация всегда была в центре внимания разработчиков интерфейсов.Ввиду растущего спроса и количества кода необходимо решение, позволяющее разделить раздутый код на небольшие модули, тем самым запустив AMD, CMD и The три модульных решения CommonJs, первое используется на стороне браузера, а последние два используются на стороне сервера, пока не появится модуль ES6.

Модуль ES6 в настоящее время не поддерживается браузерами по умолчанию. Необходимо использовать Babel. Эта ошибка часто отображается при ежедневном написании демо.

Вы можете использовать tpye="module" в теге script вТот же доменЭто может быть решено в случае с другим доменом (ситуация с другим источником будет перехвачена политикой того же происхождения, веб-шторм откроет сервер того же домена без этой проблемы, vscode, похоже, не работает)

Модуль ES6 использует ключевое слово import для импорта модулей и ключевое слово export для экспорта модулей.Он также имеет следующие характеристики.

  1. Модуль ES6 является статическим, что означает, что он запускается на этапе компиляции и имеет тот же эффект подъема, что и var и функция (эта функция позволяет ему поддерживать встряхивание дерева)

  2. Автоматически принимает строгий режим (это возвращает undefined на верхнем уровне)

  3. Модуль ES6 поддерживает использование экспорта {} для экспорта именованных интерфейсов или экспорта по умолчанию для экспорта анонимных интерфейсов.

Экспорт Module.js:

импорт a.js:

Разница между ними заключается в том, что экспорт {} экспортирует ссылку на переменную, а экспорт по умолчанию экспортирует значение.

Что это значит, то есть после импорта этих двух переменных с помощью импорта в a.js, если переменная x будет изменена в module.js по какой-то причине, это сразу отразится в a.js, а в module.js После переменная y изменена, y в a.js по-прежнему является исходным значением

module.js:

a.js:

Вы можете видеть, что для module.js установлен таймер для изменения переменных x и y через одну секунду. Через одну секунду наблюдайте за значением переменной при одновременном импорте. Вы можете обнаружить, что x был изменен, но значение y по-прежнему равно 20, потому что y экспортируется через экспорт по умолчанию, значение во время импорта эквивалентно импортируемому числу 20, а x экспортируется через экспорт {}, который экспортирует ссылку на переменную , то есть то, что импортирует a.js Текущее значение x, заботится только отокКаково значение переменной x, которую можно понимать как "живую ссылку"

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

module.js экспортирует:

импорт a.js:

Однако, поскольку модуль экспортируется в виде export {}, даже если его переименовать в значение по умолчанию, он все равно экспортирует ссылку на переменную

Вот некоторые различия между модулем ES6 и CommonJs, основными модульными решениями на данный момент.

  1. CommonJs выводит копию значения, модуль ES6 выводит ссылку на переменную через экспорт {}, а экспорт по умолчанию выводит значение

  2. CommonJs запускается на сервере и предназначен для загрузки во время выполнения, то есть код возвращается для загрузки модуля до тех пор, пока эта строка не будет выполнена, в то время как модуль ES6 представляет собой статический вывод интерфейса, который происходит на этапе компиляции.

  3. CommonJs запускается один раз и генерирует кеш при первой загрузке, после загрузки возвращается все содержимое кеша.

import()

Что касается характеристик статической компиляции модуля ES6, то ее нельзя загружать динамически, но всегда будут какие-то требования для динамической загрузки модулей, поэтому сейчас есть предложение использовать import как функцию для динамической загрузки модулей, которая возвращает Promise, когда обещание разрешено, значение - выходной модуль

Используйте метод импорта, чтобы переписать вышеуказанный a.js, чтобы его можно было загружать динамически (использование статически скомпилированного модуля ES6 в условном операторе сообщит об ошибке, потому что будет эффект улучшения, и это также не разрешено), вы можете видеть, что module.js выводит переменную x и вывод по умолчанию

Метод записи ES6 с отложенной загрузкой роутинга в Vue использует эту технологию, так что компоненты могут динамически загружаться для рендеринга представлений при переключении роутинга.

значение функции по умолчанию

ES6 позволяет устанавливать значения по умолчанию в параметрах функции

ES5 пишет:

ES6 пишет:

По сравнению с ES5 значение функции ES6 по умолчанию записывается непосредственно в параметре, что более интуитивно понятно.

Если используется параметр функции по умолчанию, в области параметров функции (внутри круглых скобок) он будет рассматриваться как отдельный параметр.область действия блока, и имеет некоторые особенности метода let/const, такие как временная мертвая зона

Здесь при запуске func, поскольку никакие параметры не передаются и используются параметры функции по умолчанию, y будет искать значение x и найдет переменную x со значением 1 во внешнем слое вдоль лексической области видимости.

Давайте посмотрим на другой пример

Здесь также не передается параметр, используется назначение функции по умолчанию, x находит переменную w через лексическую область видимости, поэтому значение x по умолчанию равно 2, y также находит только что определенную переменную x через лексическую область видимости, и значение y по умолчанию равно 3. Но при синтаксическом анализе строки z = z + 1 интерпретатор JS сначала проанализирует z+1, чтобы найти соответствующее значение, а затем присвоит его переменной z, но из-за временного отсутствия zone (let/const "hijacking" В этой области блочного уровня эту переменную нельзя использовать перед объявлением, как объяснялось выше), что приводит к использованию переменной z перед объявлением let, поэтому будет сообщено об ошибке

Это позволяет относительно легко понять значение функции по умолчанию.

Значение функции по умолчанию используется только тогда, когда входящий параметр не определен (явная передача значения undefined также приводит к использованию значения функции по умолчанию, а передача значения null не вызывает срабатывания).

В примере:

Вот пример из книги г-на Жуана Ифэна.Значением func по умолчанию является функция, которая возвращает переменную foo после выполнения.При выполнении внутри функции это эквивалентно запросу переменной (запросу LHS) для переменной foo, и запрос. Отправной точкой является эта единственная область на уровне блока, то есть интерпретатор JS не будет запрашивать переменную foo внутри функции, но сначала проверит, есть ли переменная foo в той же области (предыдущий параметр функции) по лексической области видимости, а потом искал переменную foo вне функции, но не нашел, поэтому сообщил об ошибке.Это тоже особенность дефолтного значения функции.

С помощью отладчика можно более интуитивно обнаружить, что функция func, переменная foo и this могут быть доступны через лексическую область видимости внутри этой функции, но при просмотре лексической области видимости функции func обнаруживается, что она может доступ только к Global, то есть к глобальной области видимости, переменная foo не существует в своей лексической области видимости

Значение функции по умолчанию с назначением деструктурирования

Первая строка передает функции func 2 пустых объекта, поэтому первый и второй параметры функции не будут использовать значение функции по умолчанию, а затем первый параметр функции попытается деконструировать объект и извлечь переменную x, потому что the first Первый параметр передает пустой объект, поэтому переменная x не может быть деконструирована, но во внутреннем слое установлено значение по умолчанию, поэтому значение x равно 10, а второй параметр также передает пустой объект, который будет значение по умолчанию функции, а затем она попытается деконструировать переменную y и обнаружит, что в пустом объекте нет переменной y, но y не имеет значения по умолчанию, поэтому значение y после деструктуризации равно неопределенный

Первый параметр второй строки явно передает неопределенное значение, поэтому значение по умолчанию функции будет использоваться как пустой объект, а затем попытаться деконструировать x, как в первой строке, и найти, что x не определено, но значение по умолчанию установлено, поэтому значение x равно 10, а y не определено, как указано выше.

В третьей строке оба параметра будут не определены, первый параметр такой же, как указано выше, второй параметр будет вызывать значение функции по умолчанию, присваивать его как {y:10}, а затем пытаться деконструировать переменную y, то есть у равно 10

Четвертая строка такая же, как и третья, в одной явно передается значение undefined, в другой неявно не передается никаких параметров.

Пятая строка напрямую использует входящие параметры, не использует значение функции по умолчанию и может успешно деконструировать переменные x, y

Proxy

В качестве «перехватчика» прокси может установить перехватчик перед целевым объектом. Когда другие обращаются к объекту, они должны сначала пройти через этот уровень перехватчика. Прокси также является конструктором, который использует ключевое слово new для создания экземпляра ES6 предоставляет множество операций по перехвату объекта, охватывающих практически все ситуации, которые могут изменить целевой объект (Proxy обычно используется в связке с Reflect, первый перехватывает объект, второй возвращает результат перехвата, а некоторые методы перехвата у Reflect on the Proxy есть)

Object.definePropery

Когда дело доходит до прокси, мы должны упомянуть Object.defineProperty в ES5.Этот API может добавлять свойства к объекту и дескриптор свойства/аксессор этого свойства (эти два свойства не могут сосуществовать, и одно и то же свойство может иметь только одно из них). , свойство Дескриптор имеет 4 свойства: настраиваемый, доступный для записи, перечисляемый и значение, которые определяют, является ли он настраиваемым, доступным только для чтения, перечислимым и значением свойства. получить, установить, первые две и свойство.Функции дескриптора одинаковы, последние две являются функциями, операции чтения и записи над элементами после получения и установки определены, будут выполняться следующие функции получения/установки, и по умолчанию поведение чтения и записи будет переопределено

Определено, что представление свойства a в obj доступно только для чтения и не может быть перечислено. obj2 определяет get, но не определяет set для представления только для чтения, и значение, возвращаемое при чтении свойства b obj2, является возвращаемым значением. значение функции-получателя

Что делает Object.DefineProperty в ES5, связана с прокси? Лично, я понимаю, что прокси - это расширенная версия объекта. DefineProperty. ES5 Определяет только дескрипторы свойств или доступных, которые могут определять свойства. Прокси усиливается до 13 типов, которые являются Слишком специфично. Я не буду выкладывать их за другим, здесь я дам еще несколько интересных примеров

handler.apply

apply позволяет нам перехватывать выполнение функции (JS — это функциональные объекты, прокси может перехватывать функцию), мы можем использовать его в функции регулирования

Вызовите перехваченную функцию:

handler.contruct

construct может перехватить операцию вызова этой функции через ключевое слово new, мы можем использовать его в паттерне singleton

Здесь переменная экземпляра сохраняется через замыкание. Каждый раз, когда перехваченная функция вызывается с использованием нового ключевого слова, переменная экземпляра будет проверяться. Если она существует, будет возвращена переменная экземпляра, сохраненная в замыкании. В противном случае будет возвращен новый экземпляр будет создан, который может достичь глобального только одного экземпляра

handler.defineProperty

defineProperty может перехватывать операции Object.defineProerty над этим объектом.

Обратите внимание, что операция [[SET]] по умолчанию внутри объекта (то есть присвоение свойства этому объекту) будет косвенно запускать два метода перехвата: defineProperty и getOwnPropertyDescriptor.

Вот некоторые точки знаний

  1. Здесь используется рекурсивная операция.Когда необходимо получить доступ к свойству объекта, он определит, что значение свойства объекта прокси по-прежнему является проксируемым объектом и рекурсивно прокси, в противном случае операция получения по умолчанию будет выполнена с ошибкой захватывать.
  2. Определен метод перехвата для defineProperty. Когда присваивается свойство прокси-объекта, будет выполняться операция [[SET]] по умолчанию внутри объекта для присвоения значения. Эта операция будет косвенно запускать метод defineProperty, а затем определенный будет выполнен обратный вызов.

Таким образом, независимо от того, сколько слоев объектов вложено друг в друга, пока есть свойство для назначения, будет запущен метод get, будет представлен объект этого слоя, а затем будет запущен defineProperty для выполнения обратного вызова. функция.

Другие сценарии использования

У прокси есть много других функций, например, при реализации валидатора бизнес-логика и валидатор могут быть разделены для достижения развязки, а доступ к приватной переменной может быть перехвачен с помощью get для реализации приватной переменной, перехваченный объект может быть зарегистрированы, и обещание API WeChat может быть реализовано, измениться и т. д.

Vue

Ожидается, что Youda выпустит Vue3.0 во второй половине 2019 года. Одной из основных функций является использование Proxy для замены Object.defineProperty.

Я считаю, что люди, которые немного разбираются в принципах отзывчивости Vue, знают некоторые недостатки фреймворка Vue в перехвате объектов.

<template>
   <div>
       <div>{{arr}}</div>
       <div>{{obj}}</div>
       <button @click="handleClick">修改arr下标</button>
       <button @click="handleClick2">创建obj的属性</button>
   </div>
</template>

<script>

    export default {
        name: "index",
        data() {
            return {
                arr:[1,2,3],
                obj:{
                    a:1,
                    b:2
                }
            }
        },
        methods: {
            handleClick() {
                this.arr[0] = 10
                console.log(this.arr)
            },
            handleClick2() {
                this.obj.c = 3
                console.log(this.obj)
            }
        },
   }
</script>

Вы можете видеть, что данные здесь изменились, консоль выводит новое значение, но представление не обновляется, это связано с тем, что Vue использует Object.defineProperty внутри для захвата данных, и этот API не может его обнаружить.Добавляйте и удаляйте корневые свойства объекта и присваивайте значения непосредственно индексам массива, поэтому наблюдатель рендеринга не будет уведомлен об обновлении представления, и теоретически этот API не может обнаружить серию методов массива (push, splice, pop), но фреймворк Vue модифицирует прототип массива, так что после вызова эти методы для изменения данных будут выполнять операцию обновления представления

//源码位置:src/core/observer/array.js
methodsToPatch.forEach(function (method) {
  // cache original method
  var original = arrayProto[method];
  def(arrayMethods, method, function mutator () {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    var result = original.apply(this, args);
    var ob = this.__ob__;
    var inserted;
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args;
        break
      case 'splice':
        inserted = args.slice(2);
        break
    }
    if (inserted) { ob.observeArray(inserted); }
    // notify change
    ob.dep.notify(); //这一行就会主动调用notify方法,会通知到渲染watcher进行视图更新
    return result
  });
});

существуетОтличный план перевода Nuggets на Vue3.0написано на

В версии 3.0 появится реализация наблюдателя на основе прокси, обеспечивающая полноценную реактивность по всему языку (JavaScript — аннотация), устраняющая некоторые ограничения текущей серии Vue 2 на основе Object.defineProperty, такие как: Мониторинг действий по добавлению и удалению атрибутов Модификация массивов на основе индексов, мониторинг модификаций .length Поддержка Map, Set, WeakMap и WeakSet

Прокси не имеет этой проблемы, а также предоставляет больше методов перехвата, которые могут полностью заменить Object.defineProperty Единственный недостаток — это степень поддержки браузерами (IE: кто про меня?)

Поэтому, если вы хотите иметь глубокое понимание механизма реализации Vue3.0, важно изучить Proxy.

Object.assign

Этот новый статический метод объекта ES6 позволяет нам объединять несколько объектов.

Таким образом, можно понять, что Object.assign обходит свойства объекта, который необходимо объединить с целью (то есть набор исходных объектов), используязнак равенстваНазначение, здесь пройдите {a:1}, чтобы назначить атрибут a и значение номер 1 целевому объекту, а затем пройдите {b:2}, чтобы назначить атрибут b и значение номер 2 целевому объекту.

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

  1. Object.assign — это неглубокая копия.Для свойств, значение которых является ссылочным типом, копия по-прежнему является его ссылкой.

  2. Может копировать свойства символа

  3. Невозможно скопировать неперечислимые свойства

  4. Object.assign гарантирует, что target всегда является объектом. Если передан базовый тип, он будет преобразован в базовый тип упаковки. Для null/undefined базового типа упаковки нет, поэтому при передаче будет сообщено об ошибке.

  5. Если исходный параметр является неперечислимым типом данных, слияние будет проигнорировано (строковый тип считается перечисляемым, поскольку он имеет интерфейс итератора).

  6. потому что он используетзнак равенстваДля присваивания, если свойство назначенного объекта имеет функцию установки, функция установки будет активирована Аналогично, если есть функция получения, также будет вызвана функция получения свойства назначенного объекта (поэтому Object .assign не может объединять средства доступа к свойствам объекта. Поскольку он будет напрямую выполнять соответствующие функции получения/установки вместо их объединения, если вам нужно объединить функции получения/установки свойств объекта, вы можете использовать два API-интерфейса Object.getOwnPropertyDescriptors и Object.defineProperties, предоставляемые ES7)

Вы можете видеть, что геттер/сеттер свойства a в объекте obj успешно скопирован здесь

Чтобы углубить мое понимание реализации Object.assign, которую я смоделировал, для справки

Здесь есть ямка, о которой следует упомянуть: для того, чтобы целевой параметр передавался в виде строки, он будет преобразован в базовый тип упаковки внутри, а свойства базового типа упаковки строки доступны только для чтения (доступный для записи свойство дескриптора свойства является ложным), спасибо здесьКолонна Му Ияна

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

Строка abc будет преобразована в базовый тип упаковки, а затем, когда строка def будет объединена с этим базовым типом упаковки, строка def будет расширена, и строка def будет присвоена атрибутам 0, 1, 2 базовый тип упаковки abc, а затем он сообщит об ошибке при назначении (в нестрогом режиме он будет обрабатываться только молча, а Object.assign ES6 по умолчанию включает строгий режим)

По сравнению с оператором распространения объектов ES9.

ES9 поддерживает использование операторов распространения на объектах.Функция аналогична Object.assign.Единственное отличие состоит в том, что существуют различия в свойствах объектов, содержащих функции получения/установки.

(Последнюю строку get можно игнорировать, это функция получения, запускаемая консолью для отображения переменной a)

Проанализируйте этот пример

ES9:

  • Объединит 2 объекта и вызовет только функцию получения соответствующих свойств 2 объектов.
  • Последнее из того же свойства переопределяет первое, поэтому значение свойства a является значением, возвращаемым второй функцией-получателем.

ES6:

  • Два объекта также будут объединены, и будет запущена только функция установки свойства a объекта obj без запуска функции получения (ее будет легче понять с внутренней реализацией вышеприведенного Object.assgin).
  • Функция установки свойства a в obj заменяет поведение присваивания по умолчанию, так что свойство a obj2 не будет скопировано.

За исключением случаев, когда свойства объекта имеют геттеры/сеттеры, функции Object.assgin и оператора распространения объекта одинаковы. Оба могут использоваться. Оба являются мелкими копиями. Метод использования ES9 относительно прост.

предложение

  1. Сбросить данные в данных в Vue

Это мой наиболее часто используемый трюк.Используя Object.assign, вы можете комбинировать объект данных в вашем текущем компоненте с данными в объекте данных в состоянии инициализации компонента по умолчанию, что позволяет добиться эффекта инициализации объекта данных.

В экземпляре текущего компонента свойство $data сохраняет объект данных текущего компонента, а $options — некоторые свойства при инициализации текущего экземпляра компонента.Существует метод данных, то есть функция данных, написанная на компонент, который вернется после выполнения.Инициализированный объект данных, затем объедините этот инициализированный объект данных с текущими данными, чтобы инициализировать все данные

  1. Свойства по умолчанию, необходимые для слияния объектов

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

  1. При передаче параметров несколько данных могут быть объединены в один объект и переданы в бэкэнд.

использованная литература

  1. Руан Ифэн: Начало работы со стандартами ES6

  2. МООК: ES6 Zero Basic Learning

  3. Уменьшение громкости JavaScript, о котором вы не знаете