Полное руководство по JavaScript

JavaScript

исходный адрес:freeCodeCamp(требуется лестница)

Первый перевод~ Если есть какие-то ошибки, подскажите, пожалуйста~

Полное руководство по JavaScript

JavaScript — один из самых популярных языков программирования в мире, и сейчас он широко используется вне браузера. В последние годы рост Node.js открыл область разработки серверных приложений, в которой долгое время доминировали традиционные серверные языки, такие как Java, Ruby, Python, PHP и т. д.

Это полное руководство по JavaScript следует правилу 80/20: знать JavaScript на 80% в 20% случаев.

Приходите узнать все, что вам нужно знать о JavaScript!

Уведомление: Вы можете получить это полное руководство по JavaScriptMarkdown, ePub, Mobi(код извлечения: r4i3) версия для лучшего чтения.

представлять

JavaScript — один из самых популярных языков программирования в мире. С момента своего рождения 20 лет назад он прошел долгий путь. Как первый и единственный нативный веб-браузер поддерживает язык сценариев, который находится под угрозой смерти.

В начале он не был таким мощным, как сегодня, и в основном использовался для всевозможных причудливых анимаций 😆 и динамического HTML, которым в то время восхищались (DHTML)середина. С развитием веб-платформ JavaScript такжеОбязанностьСтаньте лучше, чтобы удовлетворить потребности одной из наиболее широко используемых экосистем в мире. Сама платформа многое представила, включая API-интерфейсы браузера, и языковые функции значительно расширились.

Теперь JavaScript также может работать с базами данных и разрабатывать приложения и даже разрабатывать встроенные программы, мобильные приложения, телевизионные приложения и многое другое. Когда-то это был всего лишь небольшой язык в браузере, а теперь это самый популярный язык в мире~

Базовое определение JavaScript

JavaScript — это такой язык программирования:

  • передовой: Это позволяет вам больше сосредоточиться на собственной логике, игнорируя детали машины, на которой она в данный момент работает. JavaScript автоматически управляет памятью через сборщик мусора, позволяя вам сосредоточиться на коде, а не на управлении памятью, а также предоставляет множество конструкторов, позволяющих работать с мощными переменными и объектами.
  • динамичный: В отличие от статических языков, которые выполняются во время компиляции, динамические языки выполняются во время выполнения. У этого есть плюсы и минусы, JavaScript дает нам мощные функции, такие как: динамическая типизация, ленивая привязка, отражение, функциональное программирование, изменение объекта во время выполнения, замыкания и многое другое.
  • динамический тип: переменная не должна определять тип. Вы можете повторно привязать переменные любого типа, например привязать целочисленное значение к объявленной строковой переменной.
  • слабо типизированный: В отличие от строгой типизации, слабо типизированные языки не применяют принудительно тип объектов. Это делает операции более гибкими, но мы также не можем выполнять проверку типов для обеспечения безопасности типов (TypeScript и Flow стремятся улучшить это).
  • интерпретирующий: JavaScript обычно считается интерпретируемым языком, а это означает, что программу не нужно компилировать перед запуском, что является полной противоположностью C, Java или Go. На самом деле браузер компилирует JavaScript перед его выполнением из соображений производительности, но все это происходит естественным образом и не требует от нас дополнительных действий.
  • мультипарадигма: JavaScript не навязывает какую-либо фиксированную парадигму программирования, в отличие от Java, которая навязывает объектно-ориентированное программирование, или C, который навязывает императивное программирование. В JavaScript вы можете использовать объектно-ориентированное программирование с использованием прототипов и синтаксиса классов, представленного в ES6, вы также можете писать код в стиле функционального программирования с помощью своих первоклассных функций и даже программировать в императивном стиле (подобном C).

Просто для ясности: JavaScript не имеет ничего общего с Java, что является неудачным выбором имени, но мы должны с этим смириться.

версия JavaScript

Позвольте мне представить ECMAScript. У нас есть полное руководство, посвященное ECMAScript, где вы можете погрузиться в него, но сейчас вам просто нужно знать, что ECMAScript (также известный как ES) — это название стандарта JavaScript. JavaScript — это реализация стандарта ECMAScript, поэтому вы слышите ES6, ES2015, ES2016, ES2017, ES2018 и т. д.

Давным-давно версия JavaScript, работающая во всех браузерах, основывалась на ECMAScipt 3. Версию 4 отменили из-за расползания функционала (пытались добавить сразу много фич). Хотя ES5 — это огромная версия JavaScript, ES2015 (также известная как ES6) также является важным обновлением для JavaScript.

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

Теперь последней утвержденной версией JavaScript является ES2017.

ECMASCRIPT

Всякий раз, когда вы читаете о JavaScript, вы неизбежно видите следующие термины:

  • ES3
  • ES5
  • ES6
  • ES7
  • ES8
  • ES2015
  • ES2016
  • ES2017
  • ECMAScript 2017
  • ECMAScript 2016
  • ECMAScript 2015

что это? все они означаютСтандарт JavaScriptЭто называется Ecmascript.

ECMAScript — этоСтандарты, на которых основан JavaScript, часто именуемыйES.

Помимо JavaScript, реализации стандарта ECMAScript включают:

  • ActionScript(язык сценариев Flash), который вышел из моды после официального решения прекратить поддержку Flash с 2020 года.
  • JScript(Microsoft), вначале только Netscape поддерживал JavaScript, войны браузеров усилились, и Microsoft реализовала версию, которая поддерживала только браузер IE.

Но нет никаких сомнений в том, что JavaScript по-прежнему актуален.наиболее общийреализация ЕС.

Почему это странное имя?Ecma InternationalЯвляется ли Швейцарская ассоциация стандартов ответственной за установление международных стандартов.

Когда JavaScript был создан, Netscape и Sun Microsystems отправили его в Ecma под именем ECMA-262, также известным как ECMAScript. Согласно Википедии, совместный выпуск Netscapte и Sun Microsystems (разработчик Java)пресс-релизЭто может помочь понять причину названия, которое может включать в себя юридические вопросы и вопросы брендинга Microsoft в комитете.

После IE9 Microsoft перестала называть реализацию ES JScript в браузере и начала называть ее JavaScript (по крайней мере, я не могу найти ссылку).

Итак, начиная с 201x, единственным популярным языком, поддерживающим стандарт ECMAScript, является JavaScript.

ECMAScript последняя версия

Текущая версия ECMAScript:ES2018,Прямо сейчасES9, опубликованный в июне 2018 года.

Когда выйдет следующая версия?

Обычно JavaScript выпускает стандартную версию каждое лето, поэтому мы можем увидеть ее летом 2019 года.ECMAScript 2019(которыйES2019илиES10), но это все предположения.

Что такое ТС39

TC39 — это комитет по развитию JavaScript.

Члены TC39 включают поставщиков JavaScript и браузеров, включая Firefox, Google, Facebook, Apple, Microsoft, Intel, PayPal, SalesForce и другие.

Каждый выпуск стандартной версии должен пройтидругой этаппредложение.

ЕС версия

Я обнаружил, что версии ES иногда называют по номеру версии, а иногда по году, что может сбивать с толку.

До ES2015 стандарт ECMAScript обычно назывался по номеру версии, поэтому ES5 — это официальное название стандарта ECMAScript, обновленного в 2009 году.

Почему это происходит? Когда вышел ES2015, название сменилось с ES6 на ES2015, но было уже поздно, люди по-прежнему привычно называли ES6, и сообщество не отказалось от названия —Люди во всем мире до сих пор ссылаются на версии ES по номерам версий..

Эта таблица может помочь прояснить ваше мышление:

Версия официальное имя Дата выпуска
ES9 ES2018 июнь 2018 г.
ES8 ES2017 июнь 2017 г.
ES7 ES2016 июнь 2016 г.
ES6 ES2015 июнь 2015 г.
ES5.1 ES5.1 июнь 2011 г.
ES5 ES5 декабрь 2009 г.
ES4 ES4 заброшенный
ES3 ES3 декабрь 1999 г.
ES2 ES2 июнь 1998 г.
ES1 ES1 июнь 1997 г.

ES.Next всегда относится к будущей версии JavaScript. На момент написания этой статьи был выпущен ES9, а ES.Next — это ES10.

Улучшения ES6

ECMAScript 2015, также известный как ES6, является базовой версией стандарта ECMAScript. Так что его следует называть не ES6 (несмотря на то, что все его так называют), а ES2015.

На совершенствование ES5 ушло десять лет с 1999 по 2009 год, и хотя это был важный выпуск для языка, прошло слишком много времени, чтобы мы могли обсуждать, как работал код до ES5.

Начиная с ES5.1 и заканчивая ES6, язык JavaScript имеет важные новые функции и ключевые обновления для улучшения практики. Чтобы узнать об основных функциях ES2015, см. страницы 250–600 документа спецификации.

Важные обновления в ES2015 включают:

  • стрелочная функция
  • Promises
  • Generators
  • letа такжеconst
  • Classes
  • многострочная строка
  • строка шаблона
  • значение параметра по умолчанию
  • спред оператор
  • присваивание деструктуризации
  • Усовершенствованные литералы объектов
  • цикл for..of
  • Карта и набор

В этом руководстве я посвящу им каждую главу. Давайте начнем!

стрелочная функция

Функция стрелки Чтобы изменить привычки записи и работать большую часть кода JavaScript.

Визуально это более простые и приятные изменения, такие как:

const foo = function foo() {
  // ...
}

стали:

const foo = () => {
  // ...
}

Если в теле функции всего одна строка, то это может быть так:

const foo = () => doSomething()

Если есть только один параметр, это может быть так:

const foo = param => doSomething(param)

Он не вносит никаких несовместимых изменений по сравнению с обычными функциями и работает как прежде.

новый этот объем

в стрелочной функцииthisУнаследовано от контекста выполнения.

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

Promises

Промисы помогают нам решить известную проблему «ада обратных вызовов», хотя и создают более сложные проблемы (в ES2017 можно передавать более продвинутые конструкторыasyncрешить ).

До ES2015 разработчики JavaScript могли реализовать функциональность, подобную Promises, используя различные библиотеки (jQuery, q, deferred.js, vow...). Этот стандарт устанавливает более общий подход.

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

setTimeout(function() {
  console.log('I promised to run after 1s')
  setTimeout(function() {
    console.log('I promised to run after 2s')
  }, 1000)
}, 1000)

Эквивалентно:

const wait = () => new Promise((resolve, reject) => {
  setTimeout(resolve, 1000)
})
wait().then(() => {
  console.log('I promised to run after 1s')
  return wait()
})
.then(() => console.log('I promised to run after 2s'))

Generators

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

Сам код определяет, что он должен ждать, пока другой код запустится «в порядке очереди», и оставляет за собой право возобновить операции «по завершении ожидания». Все это с помощью простого ключевого словаyieldЧто нужно сделать. Когда генератор содержит это ключевое слово, выполнение кода будет приостановлено. Генератор может содержать множествоyieldключевое слово, так что вы можете много раз останавливаться и передавать*functionИдентификаторы ключевых слов, не путать с операторами разыменования указателя в низкоуровневых языках, таких как C, C++ или Go.

В JavaScript генераторы позволяют использовать совершенно новые парадигмы программирования, такие как:

  • Двусторонняя связь во время работы генератора
  • Постоянные циклы while, которые не останавливают программу

Вот пример, объясняющий, как работают генераторы:

function *calculator(input) {
    var doubleThat = 2 * (yield (input / 2))
    var another = yield (doubleThat)
    return (input * doubleThat * another)
}

инициализация:

const calc = calculator(10)

Затем начните перебирать генератор:

calc.next()

На первой итерации код возвращаетthisОбъект:

{
  done: false
  value: 5
}

Что произошло: Когда функция запускается,input = 10Передается в конструктор генератора как параметр, пока не встретитсяyield, вернулсяyeildСодержание:input / 2 = 5. Итак, мы получаем значение 5 и сообщаем нам, что итерация не выполнена (просто функция приостановлена).

Во второй итерации мы переходим в7:

calc.next(7)

Затем мы получаем:

{
  done: false
  value: 14
}

7даdoubleThatценность .

Примечание: вы можете подуматьinput / 2этот параметр, но это только возвращаемое значение первой итерации, на этот раз мы пропускаем этот шаг и используем новое входное значение7Умножьте на 2.

Переходим ко второй итерации, которая возвращаетdoubleThat, значение14.

Далее, последняя итерация, мы передаем 100:

calc.next(100)

вернуть:

{
  done: true
  value: 14000
}

Вся итерация заканчивается (неyeildключевое слово), мы получаем(input * doubleThat * another)Значение:10 * 14 * 100.

пусть и const

varявляется традиционнымобъем функции.

letэто новый способ объявления переменных, собласть действия блока. Это означает, что в цикле for, внутри оператора if или в обычном блоке используетсяletОбъявленная переменная не «убегает» из блока, в котором она находится, в то время какvarПеременные поднимаются в определение функции.

constа такжеlikeпохоже, ноне может быть изменен.

Заглядывая вперед к развитию JavaScript,varЗаявления исчезнут, оставив толькоletа такжеconst.

В частности, из-за неизменной природы,constСегодня он на удивление широко используется.

Classes

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

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

class Person {
  constructor(name) {
    this.name = name
  }
  hello() {
    return 'Hello, I am ' + this.name + '.'
  }
}
class Actor extends Person {
  hello() {
    return super.hello() + ' I am an actor.'
  }
}
var tomCruise = new Actor('Tom Cruise')
tomCruise.hello()

Приведенный выше код напечатает: "Hello, I am Tom Cruise. I am an actor."

Классы не объявляют явно переменные класса, вы должны инициализировать переменные в конструкторе.

Constructor

Классы имеют специальный методconstructor,В использованииnewВызывается при создании экземпляра класса.

Геттеры и сеттеры

Свойство геттера может быть объявлено следующим образом:

class Person {
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }
}

Аналогичным образом объявите свойства сеттера:

class Person {
  set age(years) {
    this.theAge = years
  }
}

модульный

До ES2015 существовало как минимум три основных модульных стандарта, разделявших сообщество:

  • AMD
  • RequireJS
  • CommonJS

ES2015 установил единый модульный стандарт.

модуль импорта

пройти черезimport ... from ...модуль импорта:

import * from 'mymodule'
import React from 'react'
import { React, Component } from 'react'
import React as MyLibrary from 'react'

модуль экспорта

вы можете использовать ключевые словаexportЭкспортируйте содержимое написанного модуля в другие модули:

export var foo = 2
export function bar() { /* ... */ }

строка шаблона

Шаблонные строки — это новый способ создания строк:

const aString = `A string`

использовать${a_variable}Синтаксис упрощает вставку значения выражения в строку:

const var = 'test'
const string = `something ${var}` //something test

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

const string = `something ${1 + 2 + 3}`
const string2 = `something ${foo() ? 'x' : 'y' }`

Строки могут быть многостроины:

const string3 = `Hey
this
string
is awesome!`

Сравните написание многострочных строк до ES2015:

var str = 'One\n' +
'Two\n' +
'Three'

значение параметра по умолчанию

Функции теперь поддерживают использование значений параметров по умолчанию:

const foo = function(index = 0, testing = true) { /* ... */ }
foo()

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

вы можете передать оператор спреда...Расширяет массив, объект или строку.

Возьмем пример с массивом:

const a = [1, 2, 3]

Вы можете создать новый массив следующим образом:

const b = [...a, 4, 5, 6]

Вы можете скопировать массив:

const c = [...a]

Это также работает для объектов, копируя объект следующим образом:

const newObj = { ...oldObj }

Для строк оператор распространения создает массив для каждого символа:

const hey = 'hey'
const arrayized = [...hey] // ['h', 'e', 'y']

Этот оператор очень полезен. Самое главное, что вы можете очень просто передать массив аргументов в функцию:

const f = (foo, bar) => {}
const a = [1, 2]
f(...a)

ранее вы могли использоватьf.apply(null, a)достигает того же эффекта, но он не очень читаем.

присваивание деструктуризации

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

const person = {
  firstName: 'Tom',
  lastName: 'Cruise',
  actor: true,
  age: 54, //made up
}
const {firstName: name, age} = person

nameа такжеageсодержит эти значения.

Этот синтаксис также можно использовать с массивами:

const a = [1,2,3,4,5]
[first, second, , , fifth] = a

Расширенные функциональные литералы

Функциональные литералы более эффективны в ES2015.

Простой синтаксис для объявления переменных

до:

const something = 'y'
const x = {
  something: something
}

Теперь вы можете:

const something = 'y'
const x = {
  something
}

прототип

Переменным можно назначать прототипы следующим образом:

const anObject = { y: 'y' }
const x = {
  __proto__: anObject
}

super()

const anObject = { y: 'y', test: () => 'zoo' }
const x = {
  __proto__: anObject,
  test() {
    return super.test() + 'x'
  }
}
x.test() //zoox

динамические свойства

const x = {
  ['a' + '_' + 'b']: 'z'
}
x.a_b //z

цикл for-of

ES5 в 2009 году представленforEach()цикл. Это хорошо, но не такforТаким образом, вы выйдете из цикла на полпути.

Представлен ES2015for-ofцикл, комбинированныйforEachПростота и возможность выскочить из петли.

//iterate over the value
for (const v of ['a', 'b', 'c']) {
  console.log(v);
}
//get the index as well, using `entries()`
for (const [i, v] of ['a', 'b', 'c'].entries()) {
  console.log(i, v);
}

Карта и набор

Mapа такжеSet(и соответствующие им слабые ссылочные типыWeakMapа такжеWeakSet) являются официальными реализациями двух очень популярных структур данных (описанных ниже).

Улучшения ES2016

ES7, официально известный как ECMAScript 2016, был выпущен в июне 2016 года.

По сравнению с ES6, ES7 представляет собой незначительное обновление версии JavaScript, включающее две функции:

  • Array.prototype.includes
  • Экспоненциальный оператор

Array.prototype.includes()

Эта функция вводит более читаемый синтаксис для проверки наличия элемента в массиве.

В ES6 и ниже, чтобы проверить, находится ли элемент в массиве, вы должны использоватьindexOfПроверить индекс массива, если возвращается-1Элемент отсутствует в массиве.

потому что-1считается истинностным значением, поэтому выневозможноРассмотрим следующий пример:

if (![1,2].indexOf(3)) {
  console.log('Not found')
}

С новым синтаксисом ES7 мы получаем то, что ожидаем:

if (![1,2].includes(3)) {
  console.log('Not found')
}

Экспоненциальный оператор

Экспоненциальный оператор**а такжеMath.pow()Последовательно, но вводит языковую функцию, а не библиотечную функцию.

Math.pow(4, 2) === 4 ** 2

Эта функция является отличным дополнением к приложениям JavaScript, интенсивно использующим математику.**Операторы стандартизированы во многих языках, таких как Python, Ruby, MATLAB, Lua, Perl и т. д.

Улучшения ES2017

ECMAScript 2017, восьмая редакция стандарта ECMA-262 (называетсяES2017илиES8), опубликованном в июне 2017 года.

По сравнению с ES6, ES8 по-прежнему предлагает множество полезных функций:

  • String padding
  • Object.values
  • Object.entries
  • Object.getOwnPropertyDescriptors()
  • запятая в конце параметра функции
  • асинхронная функция
  • Shared memory and atomics

String padding

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

ES2017 представил дваStringметод выше:padStart()а такжеpadEnd().

padStart(targetLength [, padString])
padEnd(targetLength [, padString])

пример:

padStart() выход
'test'.padStart(4) 'test'
'test'.padStart(5) ' test'
'test'.padStart(8) '    test'
'test'.padStart(8, 'abcd') 'abcdtest'
padEnd() выход
'test'.padEnd(4) 'test'
'test'.padEnd(5) 'test '
'test'.padEnd(8) 'test    '
'test'.padEnd(8, 'abcd') 'testabcd'

Object.values()

Этот метод возвращает массив, содержащий значения собственных свойств объекта. Применение:

const person = { name: 'Fred', age: 87 }
Object.values(person) // ['Fred', 87]

Object.values()Также работает с массивами:

const people = ['Fred', 'Tony']
Object.values(people) // ['Fred', 'Tony']

Object.entries()

Этот метод возвращает[key, value]Массив формы, содержащий все свойства и значения самого объекта. Применение:

const person = { name: 'Fred', age: 87 }
Object.entries(person) // [['name', 'Fred'], ['age', 87]]

Object.entries()Также работает с массивами:

const people = ['Fred', 'Tony']
Object.entries(people) // [['0', 'Fred'], ['1', 'Tony']]

getOwnPropertyDescriptors()

Этот метод возвращает все дескрипторы самого объекта (ненаследуемые).

Все объекты в JavaScript имеют набор свойств, и у каждого свойства есть дескриптор.

Дескриптор — это набор атрибутов, включающий следующие подмножества:

  • value: стоимость имущества
  • writable: свойство может быть переопределено, если оно истинно
  • get: Атрибуты функции получения, при чтении вызывается свойство
  • set: Функция сеттера свойства, при настройке значений свойств называется
  • configurable: При значении false атрибут не может быть удален, и все значения атрибута не могут быть изменены, кроме значения самого атрибута
  • enumerable: свойство перечислимо, когда оно истинно

Object.getOwnPropertyDescriptors(obj)Принимает объект в качестве параметра и возвращает объект, содержащий дескриптор.

Какая от этого польза?

ES2015 принес намObject.assign(), чтобы мы могли копировать перечисляемые свойства одного или нескольких объектов и возвращать новый объект.

Однако при этом возникает проблема: атрибуты без атрибутов по умолчанию не копируются должным образом. Например, если у объекта есть только один сеттер, его нельзя использовать.Object.assign()Скопируйте правильно.

const person1 = {
    set name(newName) {
        console.log(newName)
    }
}

Это не сработает:

const person2 = {}
Object.assign(person2, person1)

Но это работает:

const person3 = {}
Object.defineProperties(person3,
Object.getOwnPropertyDescriptors(person1))

Вы можете просто проверить это через консоль:

person1.name = 'x'
"x"
person2.name = 'x'

person3.name = 'x'
"x"

person2Без сеттера он не может быть воспроизведен правильно.

использоватьObject.create()У нас те же ограничения на объекте неглубокого клона.

замыкающая запятая

Эта функция позволяет использовать запятые в конце объявлений функций и вызовов функций:

const doSomething = (var1, var2,) => {
  //...
}
doSomething('test2', 'test2',)

Это изменение побудит разработчиков перестать использовать уродливую привычку «запятая в начале строки».

асинхронная функция

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

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

почему они полезны

Это высокоуровневая абстракция над функцией promises.

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

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

короткий пример

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

function doSomethingAsync() {
    return new Promise((resolve) => {
        setTimeout(() => resolve('I did something'), 3000)
    })
}
async function doSomething() {
    console.log(await doSomethingAsync())
}
console.log('Before')
doSomething()
console.log('After')

Приведенный выше код будет напечатан в консоли браузера:

Before
After
I did something //after 3s

Объединение нескольких асинхронных функций

Цепочка асинхронных функций проста и более читабельна, чем необработанные промисы:

function promiseToDoSomething() {
    return new Promise((resolve)=>{
        setTimeout(() => resolve('I did something'), 10000)
    })
}
async function watchOverSomeoneDoingSomething() {
    const something = await promiseToDoSomething()
    return something + ' and I watched'
}
async function watchOverSomeoneWatchingSomeoneDoingSomething() {
    const something = await watchOverSomeoneDoingSomething()
    return something + ' and I watched as well'
}
watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => {
    console.log(res)
})

Общая память и атомарность

WebWorkers используются для создания многопоточных программ в браузере.

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

Поскольку мы не знаем, сколько времени потребуется событиям для записи части доставки в разделяемую память,атом(Atomics) — это метод, который выполняет операцию при чтении значения и выполнении всех типов операций записи.

Больше можно найти наэто предложение, предложение реализовано.

Улучшения ES2018

ES2018 — это последний стандарт ECMAScript.

Что нового он вводит?

Свойства отдыха/спреда

ES6 представилдеструктуризация массиваизостальные элементы:

const numbers = [1, 2, 3, 4, 5]
[first, second, ...others] = numbers

а такжеэлемент расширения:

const numbers = [1, 2, 3, 4, 5]
const sum = (a, b, c, d, e) => a + b + c + d + e
const sum = sum(...numbers) // 勘误:此处不能对 sum 重新赋值,感谢 [森蓝情丶](https://juejin.cn/user/3773179636225895)

ES2018 привносит ту же функциональность в объекты.

остальные свойства:

const { first, second, ...others } = { first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }
first // 1
second // 2
others // { third: 3, fourth: 4, fifth: 5 }

Расширенные атрибутыПозволяет создавать новые объекты, комбинируя свойства объектов, переданных после оператора распространения:

const items = { first, second, ...others }
items //{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }

Асинхронная итерация

новый конструкторfor-await-ofПозволяет использовать асинхронные итерации в качестве итераций цикла:

for await (const line of readLines(filePath)) {
  console.log(line)
}

потому что здесьawait, так что можно толькоasyncиспользуйте его внутри функции, как обычныйawait(См. асинхронный/ожидание).

Promise.prototype.finally()

Когда обещание выполнено, оно будет вызываться одно за другимthen()метод. Если во время этого процесса возникают какие-либо ошибки,then()Метод будет пропущен и выполненcatch()метод.

Удастся ли обещание или нет,finally()запустит в нем код:

fetch('file.json')
  .then(data => data.json())
  .catch(error => console.error(error))
  .finally(() => console.log('finished'))

Улучшения регулярных выражений

Пост-утверждение RegExp: соответствует строке на основе предыдущего содержимого.

Это предварительное утверждение: используйте?=Совпадение с определенной подстрокой:

/Roger(?=Waters)/
/Roger(?= Waters)/.test('Roger is my dog') //false
/Roger(?= Waters)/.test('Roger is my dog and Roger Waters is a famous musician') //true

?!Сделайте наоборот, сопоставив строку посленетКонкретные подстроки:

/Roger(?!Waters)/
/Roger(?! Waters)/.test('Roger is my dog') //true
/Roger(?! Waters)/.test('Roger Waters is a famous musician') //false

Использовать опережающее утверждение?=идентификатор, который уже имеется.

ретроспективное утверждение, новая функция, используйте?<=.

/(?<=Roger) Waters/
/(?<=Roger) Waters/.test('Pink Waters is my dog') //false
/(?<=Roger) Waters/.test('Roger is my dog and Roger Waters is a famous musician') //true

использовать?<!Отрицательное ретроспективное утверждение:

/(?<!Roger) Waters/
/(?<!Roger) Waters/.test('Pink Waters is my dog') //true
/(?<!Roger) Waters/.test('Roger is my dog and Roger Waters is a famous musician') //false

Экранирование свойства Unicode\p{…}а также\P{…}

В регулярном выражении вы можете использовать\dсоответствует любому числу,\sсоответствует любому символу, исключая пробелы,\wсоответствует любому буквенно-цифровому символу и т. д.

Эта новая функция представлена\p{}а также\P{}Распространите эту концепцию на все символы Unicode.

Любой символ Юникода имеет определенный набор свойств. Например,Scriptопределяет языковую семью,ASCIIявляется ли логическое значение строки ASCII истинным и т. д. Вы можете поместить эту функцию в фигурные скобки, и регулярное выражение проверит ее на истинность:

/^\p{ASCII}+$/u.test('abc')   //✅
/^\p{ASCII}+$/u.test('ABC@')  //✅
/^\p{ASCII}+$/u.test('ABC🙃') //❌

ASCII_Hex_Digit— еще одно логическое свойство, которое проверяет, содержит ли строка допустимое шестнадцатеричное число:

/^\p{ASCII_Hex_Digit}+$/u.test('0123456789ABCDEF') //✅
/^\p{ASCII_Hex_Digit}+$/u.test('h')                //❌

Есть также много логических свойств, которые можно использовать, просто поместив их имена в фигурные скобки, например:Uppercase, Lowercase, White_Space, Alphabatic, Emojiтак далее:

/^\p{Lowercase}$/u.test('h') //✅
/^\p{Uppercase}$/u.test('H') //✅
/^\p{Emoji}+$/u.test('H')   //❌
/^\p{Emoji}+$/u.test('🙃🙃') //✅

В дополнение к этим двоичным свойствам вы также можете проверить, соответствует ли какой-либо символ Юникода определенному значению. В этом примере я проверяю, является ли строка греческой или латинской:

/^\p{Script=Greek}+$/u.test('ελληνικά') //✅
/^\p{Script=Latin}+$/u.test('hey') //✅

Чтобы узнать больше, вы можете прочитатьпредложение.

именованная группа захвата

В ES2018 совпадающая группа может быть привязана к имени, а не просто к слоту в массиве результатов:

const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const result = re.exec('2015-01-02')
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';

регулярные выражения'

sдаодна линияАббревиатура от , которую можно сочетать с.Сопоставлять новые строки вместе, без этого идентификатор точки не будет соответствовать новым строкам:

/hi.welcome/.test('hi\nwelcome') // false
/hi.welcome/s.test('hi\nwelcome') // true

стиль программирования

Стиль программирования JavaScript — это набор соглашений при написании JavaScript.

стиль программированияты и твоя командаДостигнуто соглашение для обеспечения согласованности проекта. Если у вас нет команды, то этосамиКонтракт всегда должен гарантировать, что ваш код соответствует вашим стандартам.

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

популярные руководства по стилю

В JavaScript существует множество руководств по стилю, но наиболее распространенными являются следующие:

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

Сохраняйте стиль в соответствии с вашим проектом

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

Каждый проект с открытым исходным кодом на Github может иметь набор правил, а другой проект, над которым вы работаете, может иметь совершенно другие правила.

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

Правила, используемые в этом руководстве

Мы использовали последнюю версию ES и Babel в старых браузерах.

  • отступ: замена табуляции пробелами, отступ на два пробела.

  • точка с запятой: Не используйте точку с запятой.

  • длина каждой строки: Если возможно, старайтесь, чтобы длина каждой строки не превышала 80 символов.

  • Встроенные комментарии: Используйте встроенные комментарии в коде, блочные комментарии только в файлах.

  • нет бесполезного кода: Не оставляйте старый код, "на всякий случай" он пригодится в будущем. Гарантировано, что у вас будет только тот код, который вам нужен сейчас, контроль версий или ваше приложение для создания заметок созданы для этого.

  • Аннотировать только при необходимости: Не добавляйте заметки, которые не могут помочь понять. Если в самом коде есть хорошая переменная, имя функции и комментарий к функции JSDoc, не добавляйте комментарий.

  • объявление переменной: избегайте загрязнения глобальных переменных, никогда не используйтеvar. Использовать по умолчаниюconst, используется только тогда, когда вам нужно повторно связать переменныеlet.

  • постоянный: объявляйте все константы заглавными буквами, используйте_сегментацияVARIABLE_NAME.

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

    const test = (a, b) => a + b
    const another = a => a + 2
    
  • имя: Имена функций, имена переменных и имена методов должны начинаться со строчной буквы (если они не являются частными и доступными только для чтения) в верблюжьем регистре, только имена конструкторов и классов должны начинаться с прописной буквы. Если вы используете фреймворк с определенными спецификациями, измените свои привычки по мере необходимости. Имя файла должно быть все в нижнем регистре, используйте-сегментация.

  • Конкретные форматы заявлений и правила:

    if

    if (condition) {
      statements
    }
    if (condition) {
      statements
    } else {
      statements
    }
    if (condition) {
      statements
    } else if (condition) {
      statements
    } else {
      statements
    }
    

    for: Всегда кэшируйте длину объекта обхода при инициализации, не вставляйте его в условный оператор. избегать использованияfor inвыражения, кроме и.hasOwnProperty()Используется вместе, предпочтительноfor of:

    for (initialization; condition; update) {
      statements
    }
    

    while

    while (condition) {
      statements
    }
    

    do

    do {
      statements
    } while (condition);
    

    switch

    switch (expression) {
      case expression:
        statements
      default:
        statements
    }
    

    try

    try {
      statements
    } catch (variable) {
      statements
    }
    try {
      statements
    } catch (variable) {
      statements
    } finally {
      statements
    }
    
  • пространство: такт для улучшения читаемости кода за счет пробелов: между ключевыми словами и(вставляет пробел; в бинарном операторе (+, -, / ,*, &&...) вставить пробел до и после; внутри оператора каждый;Вставьте пробел после каждого оператора, чтобы отделить каждый оператор; после каждого,Вставьте пробел после него.

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

  • кавычки: используйте одинарные кавычки'вместо двойных кавычек". Двойные кавычки являются стандартным атрибутом HTML, поэтому используйте одинарные кавычки, чтобы избежать возможных проблем при работе со строками HTML. При необходимости используйте строки шаблона вместо интерполяции переменных.

Лексическая структура

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

Unicode

JavaScript написан в Unicode. Это означает, что вы можете использовать эмодзи в качестве имен переменных. 😃 😧 😲 Что еще более важно, вы можете писать идентификаторы на любом языке, например, на японском или китайском,Связанные правила.

точка с запятой

Синтаксис JavaScript похож на язык C, и во многих примерах кода вы можете увидеть точку с запятой в конце каждой строки кода.

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

Вам просто нужно избегать странных практик, таких как разделение операторов на несколько строк:

return
variable

или перед строкой с ([или()начало. Таким образом, вы в безопасности 99,9% случаев (ваш линтер тоже предупреждает вас).

Это зависит от личных предпочтений, недавно я решилНет больше бесполезных точек с запятой, поэтому в этом посте вы не увидите точек с запятой.

пространство

JavaScript не считает пробелы значимыми. Пробелы и разрывы строк могут быть добавлены по вашему вкусу, даже еслитеоретическиОно работает.

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

Например, мне нравится делать отступ на два символа.

Деликатный случай

JavaScript чувствителен к регистру. Переменнаяsomethingа такжеSomethinотличается. Это согласуется с любым идентификатором.

Примечания

В JavaScript есть два типа комментариев:

/* */
//

Первый может быть многострочным комментарием и должен быть закрыт.

Второй закомментирует все справа от текущей строки.

Литералы и идентификаторы

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

5
'Test'
true
['a', 'b']
{color: 'red', shape: 'Rectangle'}

ОдинидентификаторМожет использоваться для идентификации переменной, функции, объекта. его можно использовать со знаком доллара$или подчеркивание_Он также может содержать цифры в начале. В Unicode буквы могут быть любыми разрешенными символами, например, эмодзи :smile:.

Test
test
TEST
_test
Test1
$test

Знаки доллара часто используются для различения элементов DOM.

зарезервированное слово

Вы не можете использовать следующие идентификаторы, поскольку они являются зарезервированными словами языка.

break
do
instanceof
typeof
case
else
new
var
catch
finally
return
void
continue
for
switch
while
debugger
function
this
with
default
if
throw
delete
in
try
class
enum
extends
super
const
export
import
implements
let
private
public
interface
package
protected
static
yield

Переменная

Переменная — это идентификатор, связанный с литералом, поэтому вы можете ссылаться на него и использовать его в последующем коде. Мы узнаем, как объявить переменную в JavaScript.

Знакомство с переменными JavaScript

Переменная — это идентификатор, связанный с литералом, поэтому вы можете ссылаться на него и использовать его в последующем коде. В JavaScript переменные не привязаны ни к какому типу. Даже если вы привязываете конкретный тип к переменной, вы можете позже повторно привязать любой другой тип, не вызывая ошибок типа или других проблем.

Вот почему JavaScript иногда называют «нетипизированным».

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

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

До ES2015,varэто единственный способ определить переменные.

var a = 0

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

Если вы объявите переменную без инициализации, она получитundefinedзначение, пока вы не привяжете к нему значение.

var a //typeof a === 'undefined'

Вы можете многократно объявлять одну и ту же переменную, переопределяя ее значение:

var a = 1
var a = 2

Вы можете объявить несколько переменных одновременно:

var a = 1, b = 2

кодобъемэто диапазон, в котором переменная видна.

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

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

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

использовать пусть

letЭто новая функция, представленная в ES2015, которая по сути является блочной.var. Его объем ограничен определением его блока, оператора или выражения, а также все внутренние блоки, содержащиеся.

Современные разработчики JavaScript могут просто использоватьletполностью отказаться от использованияvar.

еслиletКажется, расплывчатый термин, просто посмотритеlet color = 'red', сделайте цвет красным, что гораздо легче понять.

использовать вне функцииlet,а такжеvarВместо этого глобальная переменная не создается.

использовать константу

использоватьvarилиletОбъявленные переменные можно изменить и повторно привязать позже. однаждыconstПосле инициализации его значение не может быть изменено или привязано к другим значениям.

const a = 'test'

мы не можемa绑定不同的值。 Однако, еслиaявляется объектом, который предоставляет методы для изменения своего содержимого, мы все равно можем его изменить.

constНе обеспечивает неизменности, только гарантирует, что ссылка не будет изменена.

constа такжеletАналогичным образом обеспечивается область видимости на уровне блоков.

Теперь разработчики JavaScript могут выбиратьconstКак идентификатор для объявления переменных, которые не нужно перепривязывать в будущем.

Почему? Потому что мы всегда должны использовать самые простые переменные конфигурации, чтобы избежать ошибок.

Типы

Иногда вы будете читать, что JS нетипизирован, но это не так. Это правда, что к переменной можно привязать разные типы, но в JavaScript есть типы. Он предоставляет примитивные типы и типы объектов.

базовый тип

Примитивные типы:

  • Numbers
  • Strings
  • Booleans

Есть также два специальных типа:

  • null
  • undefined

Давайте углубимся в них в следующей главе.

Numbers

Внутренне JavaScript имеет только один тип чисел: все числа являются числами с плавающей запятой.

Числовой литерал — это число, представленное в исходном коде, amd может быть либо целочисленным литералом, либо литералом с плавающей запятой, в зависимости от того, как оно написано.

Целое:

10
5354576767321
0xCC //hex

Плавать:

3.14
.1234
5.2e4 //5.2 * 10^4

strings

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

'A string'
"Another string"

Строки могут занимать несколько строк с обратной косой чертой:

"A \
string"

Строки могут содержать escape-последовательности, которые интерпретируются при печати строки, например, \n для новой строки. Обратная косая черта также полезна, когда вам нужно предотвратить неправильное толкование символов как закрывающие кавычки:

'I\'m a developer'

Строки можно объединять с помощью оператора +:

"A " + "string"

строка шаблона

Представленные в ES2015 строки-шаблоны обеспечивают более мощный способ определения строкового литерала.

`a string`

Вы можете внедрить любое выражение JavaScript и заменить результат после выполнения:

`a string with ${something}`
`a string with ${something+somethingElse}`
`a string with ${obj.something()}`

Вы можете легко получить многострочные строки:

`a string
with
${something}`

Booleans

Для логических значений в JavaScript есть два зарезервированных слова: true и false. большинство операторов сравнения== === > <В ожидании одного из них.

if,whileОператоры и другие управляющие структуры используют логические значения для определения потока программы.

Они не только принимают истину и ложь, они также принимаютtruthyа такжеfalsyценность .

Falsy, значение интерпретируется какfalse:

0
-0
NaN
undefined
null
'' //empty string

Остальные принадлежатtruthy.

null

nullявляется специальным значением, которое означает отсутствие значения. Это также распространенное понятие в других языках, таких какnilили на питонеNone.

undefined

undefinedУказывает, что переменная не была инициализирована и ее значение пусто.

Не работаетreturnОн вернет значениеundefined. Он также не определен, если вызывающая сторона не присвоила параметру функции значение.

Проверьте, является ли значениеundefined, вы можете использовать этот метод:

typeof variable === 'undefined'

тип объекта

Все, что не является примитивным типом, является объектным типом.

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

выражение

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

арифметическое выражение

Все выражения в этой категории оценивают числа:

1 / 2
i++
i -= 2
i * 2

строковое выражение

Оценить строку:

'A ' + 'string'
'A ' += 'string'

оригинальное выражение

В эту категорию входят ссылки на переменные, литералы и константы:

2
0.02
'something'
true
false
this //the current object
undefined
i //where i is a variable or a constant

Есть также некоторые ключевые слова языка:

function
class
function* //the generator function
yield //the generator pauser/resumer
yield* //delegate to another generator or iterator
async function* //async function expression
await //async function pause/resume/wait for completion
/pattern/i //regex
() // grouping

Инициализаторы массивов и объектов

[] //array literal
{} //object literal
[1,2,3]
{a: 1, b: 2}
{a: {b: 1}}

логическое выражение

Логические выражения используют логические операторы для получения логического значения:

a && b
a || b
!a

Левосторонние выражения

new //create an instance of a constructor
super //calls the parent constructor
...obj //expression using the spread operator

выражение доступа к свойству

object.property //reference a property (or method) of an object
object[property]
object['property']

Выражение конструктора объекта

new object()
new a(1)
new MyRectangle('name', 2, {a: 4})

выражение определения функции

function() {}
function(a, b) { return a * b }
(a, b) => a * b
a => a * 2
() => { return 2 }

выражение вызова

a.x(2)
window.resize()

прототипное наследование

JavaScript занимает особое место среди популярных языков программирования из-за использования прототипного наследования. Давайте посмотрим, что это значит.

Большинство объектно-ориентированных языков основаны на шаблоне наследования классов, JavaScript основан на прототипном наследовании.

Что это значит?

Каждый объект JavaScript имеет свойство, называемоеprototype, указывая на другой объект.

Этот другой объектпрототип объекта.

Наш объект наследует свойства и методы объекта-прототипа.

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

const car = {}

или черезnew ObjectСоздайте:

const car = new Object()

Так или иначе,carПрототипыObject.

Инициализируйте массив, который также является объектом:

const list = []
//or
const list = new Array()

Прототипом этого являетсяArray.

ты можешь пройти__proto__Проверка атрибута:

car.__proto__ == Object.prototype //true
car.__proto__ == new Object().__proto__ //true
list.__proto__ == Object.prototype //false
list.__proto__ == Array.prototype //true
list.__proto__ == new Array().__proto__ //true

здесь__proto__Свойства не являются стандартными, но широко реализованы в браузерах. Более надежный способ получить прототипObject.getPrototypeOf(new Object()).

Все функции и методы прототипа доступны для объектов с этим прототипом:

Object.prototypeявляется прототипом всех объектов.

Array.prototype.__proto__ == Object.prototype

Если вам интересно, что такое прототип Object.prototype, у него нет прототипа. Это особенная снежинка. ❄️

Приведенный выше примерСеть прототиповпример.

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

помимо использованияnewЧтобы создать объект или использовать литеральный синтаксис для объектов и массивов, вы также можете использоватьObject.create()Создание экземпляра объекта.

Первый параметр как прототип объекта:

const car = Object.create({})
const list = Object.create(Array)

ты можешь использовать этоisPrototypeOf()Метод проверяет прототип этого объекта:

Array.isPrototypeOf(list) //true

Обратите внимание, потому что вы можете создавать экземпляры массивов следующим образом:

const list = Object.create(Array.prototype)

следовательно,Array.isPrototypeOf(list)равно false, аArray.prototype.isPrototypeOf(list)равно истинному.

Добрый

Классы были представлены в стандарте ECMAScript 6 (ES6), выпущенном в 2015 году.

До этого JavaScript мог реализовать наследование только очень странным образом. Это прототипное наследование, и я думаю, что это удивительно, в отличие от других популярных языков.

Люди из Java, Python или других языков с трудом понимают сложности прототипного наследования, поэтому комитет ECMAScript решил ввести синтаксический сахар на основе этого, аналогично тому, как это реализуют другие языки.

Это важно: лежащий в основе JavaScript остается таким же, как и раньше, и вы можете обращаться к объектам-прототипам универсальным способом.

определить класс

Класс выглядит так:

class Person {
  constructor(name) {
    this.name = name
  }
  hello() {
    return 'Hello, I am ' + this.name + '.'
  }
}

Класс имеет идентификатор, который позволяет нам передатьnew ClassIdentifier()Создайте новый объект.

Когда этот объект будет создан, он будет вызыватьсяconstructorможно передавать любые параметры.

У класса может быть много методов, в этом примереhelloэто метод, который может быть вызван всеми объектами, производными от этого класса:

const flavio = new Person('Flavio')
flavio.hello()

наследование классов

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

Если унаследованный класс и класс более высокого уровня имеют метод с одинаковым именем, они вызываются по принципу ближайшего первого:

class Programmer extends Person {
  hello() {
    return super.hello() + ' I am a programmer.'
  }
}
const flavio = new Programmer('Flavio')
flavio.hello()

Вышеупомянутая программа распечатает: "Hello, I am Flavio. I am a programmer.".

Классы не объявляют переменные явно, но они должны быть инициализированы в конструкторе.

В классе вы можете сделать это, позвонивsuper()Представьте родительский класс.

статический метод

Вообще говоря, методы определяются для экземпляров, а не для классов.

И статические методы могут быть выполнены в классе:

class Person {
  static genericHello() {
    return 'Hello'
  }
}
Person.genericHello() //Hello

частный метод

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

Геттеры и сеттеры

можете добавитьgetилиsetПрефикс создает геттер и сеттер, какой метод вы используете, зависит от того, что вы хотите сделать: получить доступ к переменной или изменить значение переменной.

class Person {
  constructor(name) {
    this.name = name
  }
  set name(value) {
    this.name = value
  }
  get name() {
    return this.name
  }
}

Если свойство имеет только один геттер, свойство не может быть установлено, и все изменения игнорируются:

class Person {
  constructor(name) {
    this.name = name
  }
  get name() {
    return this.name
  }
}

Если свойство имеет только один сеттер, вы можете изменить его значение по желанию, но доступ к нему извне невозможен:

class Person {
  constructor(name) {
    this.name = name
  }
  set name(value) {
    this.name = value
  }
}

аномальный

Когда в вашем коде возникает непредвиденная ошибка, JavaScript используется для обработки ошибок посредством исключений.

Создать исключение

по ключевому словуthrowСоздать исключение:

throw value

valueМожет быть любым значением JavaScript, включая строку, число или объект.

Когда код JavaScript выполнится до этой строки, нормальный ход программы будет приостановлен, и управление перейдет к ближайшей строке.обработчик исключений.

Обработка исключений

Обработчик исключенийtry/catchутверждение.

tryИсключения, созданные в блоке кода, будутcatchОбработка в:

try {
  //lines of code
} catch (e) {
  
}

В этом примереeобозначает выбросы.

Вы можете добавить несколько обработчиков, которые перехватывают разные ошибки.

finally

Чтобы завершить весь оператор кода, в JavaScript есть еще один оператор,finally, вне зависимости от хода программы, обрабатывается исключение или нет, код в нем будет выполнен:

try {
  //lines of code
} catch (e) {
  
} finally {
  
}

Можно и безcatchиспользовать в случаеfinally, удалить можноtryЛюбой ресурс, например файл или сетевой запрос, открытый в:

try {
  //lines of code
} finally {
}

Вложенные блоки try

tryМожет быть вложенным, исключения всегда обрабатываются в ближайшем блоке catch:

try {
  //lines of code
  try {
    //other lines of code
  } finally {
    //other lines of code
  }
} catch (e) {
  
}

если внутреннийtryИсключение находится во внешнемcatchобработано в.

точка с запятой

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

Точка с запятой создала большой раскол в сообществе JavaScript. Некоторым людям нравится использовать его независимо от ситуации, а другим нет.

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

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

Поскольку в JavaScript не используются точки с запятой, все возможно. Когда для позиции требуется точка с запятой, она добавляется автоматически.

Процесс выполнения этогоАвтоматически вставлять точки с запятой.

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

Правило автоматической вставки точки с запятой в JavaScript

Во время интерпретации исходного кода интерпретатор JavaScript автоматически добавит точку с запятой при обнаружении конкретной ситуации:

  • Начальный код следующей строки разрывает текущую строку (код может состоять из нескольких строк)
  • Следующая строка кода начинается с}в начале закрытие текущего блока кода
  • когда выполнение достигает конца исходного кода
  • Текущая строка имеетreturnутверждение
  • Текущая строка имеетbreakутверждение
  • Текущая строка имеетthrowутверждение
  • Текущая строка имеетcontinueутверждение

Примеры непоследовательного поведения кода

Основываясь на приведенных выше правилах, вот несколько других примеров, таких как:

const hey = 'hey'
const you = 'hey'
const heyYou = hey + ' ' + you
['h', 'e', 'y'].forEach((letter) => console.log(letter))

ты увидишь ошибкуUncaught TypeError: Cannot read property 'forEach' of undefined, потому что правило1Попробуйте интерпретировать код как:

const hey = 'hey';
const you = 'hey';
const heyYou = hey + ' ' + you['h', 'e', 'y'].forEach((letter) => console.log(letter))

а также:

(1 + 2).toString() // 3
const a = 1
const b = 2
const c = a + b
(a + b).toString()

Вместо этого приведенный выше код выдаетTypeError: b is not a functionисключение, потому что JavaScript пытается интерпретировать его как:

const a = 1
const b = 2
const c = a + b(a + b).toString()

Другой пример правила 4:

(() => {
  return
  {
    color: 'white'
  }
})()

Вы хотите, чтобы эта непосредственная функция возвращалаcolorсвойства объекта, но нет. то, что он возвращаетundefined, потому что JavaScript будетreturnПосле вставки запятой.

Правильный подход - поставить фигурные скобкиreturnзадняя часть:

(() => {
  return {
    color: 'white'
  }
})()

Вы думаете, что этот код выдаст 0:

1 + 1
-1 + 1 === 0 ? alert(0) : alert(2)

Но он показывает 2, потому что правило 1 интерпретирует его как:

1 + 1 -1 + 1 === 0 ? alert(0) : alert(2)

Будь осторожен. Некоторые люди параноидально относятся к точкам с запятой, мне все равно, поскольку этот инструмент дает нам возможность не использовать точки с запятой, мы должны избегать точек с запятой. Я ничего не рекомендую, вы выбираете.

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

Запомните эти правила:

  • будьте осторожныreturnутверждение. Если вы хотите что-то вернуть, это должно быть в той же строке, что и return (break, throw, continueтоже самое)
  • Никогда не начинайте со круглых скобок, которые могут быть объединены с предыдущей строкой для формирования вызова функции или ссылки на элемент массива.

Наконец, всегда проверяйте свой код, чтобы убедиться, что он соответствует вашим требованиям.

кавычки

Теперь поговорим о кавычках и особенностях JavaScript.

JavaScript допускает три формы кавычек:

  • апостроф
  • Двойные кавычки
  • обратные кавычки

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

const test = 'test'
const bike = "bike"

Использование того или другого не имеет большого значения. Единственное отличие состоит в том, что символ кавычки, используемый для разделения строки, должен быть экранирован:

const test = 'test'
const test = 'te\'st'
const test = 'te"st'
const test = "te\"st"
const test = "te'st"

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

Я лично использую одинарные кавычки все время и только двойные кавычки в HTML.

Обратные кавычки — еще один вариант, представленный в ES6 в 2015 году.

Все они имеют специальную функцию — возможность многострочных строк.

Многострочные строки могут быть обычными строками плюс escape-символы:

const multilineString = 'A string\non multiple lines'

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

const multilineString = `A string
on multiple lines`

Не только это. ты можешь использовать это${}Разобрать переменные:

const multilineString = `A string
on ${1+1} lines`

это также называетсястрока шаблона.

строка шаблона

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

Строки шаблонов как функция ES2015/ES6 позволяют работать со строками более новыми способами, чем ES5 и более ранние версии.

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

const a_string = `something`

Они обеспечивают больше функциональности, чем обычные строки, в частности:

  • Обеспечивает отличный синтаксис для определения многострочных строк.
  • Предоставляет простой способ интерпретации переменных и выражений в строках.
  • Позволяет создавать DSL с помощью тегов шаблона

Давайте подробнее рассмотрим эти особенности.

многострочная строка

До ES6 для создания многострочных строк требовалось добавлять в конце каждой строки\персонаж:

const string = 'first part \
second part'

Это выглядит как двухстрочная строка, но при рендеринге становится одной строкой:

"first part second part"

Чтобы иметь возможность отображать многострочные строки, вам нужно добавить в конце каждой строки\n:

const string = 'first line\n \
second line'

или

const string = 'first line\n' +
               'second line'

Строки-шаблоны упрощают создание многострочных строк:

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

const string = `Hey
this

string
is awesome!`

Примечание: пробелы также имеют смысл, сделайте это:

const string = `First
                Second`

Создаю такую ​​строку:

First
                Second

У этой проблемы есть простое решение: оставьте первую строку пустой и добавьте закрывающую обратную кавычку с помощьюtrim()метод, который игнорирует все пробелы перед первым символом:

const string = `
First
Second`.trim()

интерполяция

Строки-шаблоны обеспечивают простой способ вставки переменных и выражений в строки.

вы можете использовать${...}грамматика:

const var = 'test'
const string = `something ${var}` //something test

существует${}Вы можете добавить что угодно, даже выражения:

const string = `something ${1 + 2 + 3}`
const string2 = `something ${foo() ? 'x' : 'y' }`

Теги шаблона

Теги шаблонов — это функция, которая на первый взгляд может показаться не очень полезной, но на самом деле она используется многими популярными библиотеками, такими какStyled Components,Apollo,GraphQLБиблиотека клиент/сервер, поэтому важно понимать, как она работает.

В Styled Components теги шаблона используются для определения CSS:

const Button = styled.button`
  font-size: 1.5em;
  background-color: black;
  color: white;
`;

В Apollo теги шаблонов используются для определения таблиц запросов GraphQL:

const query = gql`
  query {
    ...
  }
`

выделены в этих примерахstyled buttonа такжеgqlТеги шаблонафункция:

function gql(literals, ...expressions) {
  
}

Эта функция возвращает строку, которую можноЛюбыеРезультаты расчета.

literalsпредставляет собой массив тегов шаблона, содержащий теги, которые интерполируются выражениями.

expressionsСодержит все интерполяции.

Например, приведенный выше пример:

const string = `something ${1 + 2 + 3}`

literalsМассив содержит два элемента. первый изsomethingНачинать до тех пор, пока не встретится первая интерполированная строка, вторая — пустая строка, пробел между концом первой интерполяции (у нас есть только одна) и концом всей строки.

Более сложный пример:

const string = `something
another ${'x'}
new line ${1 + 2 + 3}
test`

В этом примереliteralsПервый элемент массива:

`something
another `

Второй пункт:

`
new line `

Третий пункт:

`
test`

expressionsВключеноxа также6Множество.

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

Простейшим примером является функция присвоения интерполированной строки путем добавленияliteralsа такжеexpressions:

const interpolated = interpolate`I paid ${10}€`

interpolateэто:

function interpolate(literals, ...expressions) {
  let string = ``
  for (const [i, val] of expressions) {
    string += literals[i] + val
  }
  string += literals[literals.length - 1]
  return string
}

Функция JavaScript

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

Все в JavaScript является функцией.

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

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

Функции в JavaScript такжеобъект, специальный объект:функциональный объект. Их суперсила в том, что их можно призвать.

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

Начнем со «старого» синтаксиса до ES6. Этодекларативная функция:

function dosomething(foo) {
  // do something
}

Теперь, в ES6/ES2015, он принадлежитобычная функция.

Функция может быть привязана к переменной (это называетсяфункциональное выражение):

const dosomething = function(foo) {
  // do something
}

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

const dosomething = function dosomething(foo) {
  // do something
}

Представлены ES6/ES2015стрелочная функция, которая полезна как встроенная функция в качестве параметра или обратного вызова:

const dosomething = foo => {
  //do something
}

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

параметр

Функция может иметь один или несколько параметров.

const dosomething = () => {
  //do something
}

const dosomethingElse = foo => {
  //do something
}

const dosomethingElseAgain = (foo, bar) => {
  //do something
}

Запуск ES6/ES2015, параметры функции могут иметь значения по умолчанию:

const dosomething = (foo = 1, bar = 'hey') => {
  //do something
}

Это позволяет вам вызывать вызывающую функцию без заполнения аргументов:

dosomething(3)
dosomething()

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

const dosomething = (foo = 1, bar = 'hey') => {
  //do something
}
dosomething(2, 'ho!')

Вы можете обернуть все параметры в массив, а затем использовать оператор расширения для расширения при вызове:

const dosomething = (foo = 1, bar = 'hey') => {
  //do something
}
const args = [2, 'ho!']
dosomething(...args)

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

const dosomething = ({ foo = 1, bar = 'hey' }) => {
  //do something
  console.log(foo) // 2
  console.log(bar) // 'ho!'
}
const args = { foo: 2, bar: 'ho!' }
dosomething(args)

возвращаемое значение

Каждая функция возвращает значение, по умолчаниюundefined.

Все функции завершаются после выполнения последней строки кода или до тех пор, покаreturnключевые слова. Когда JavaScript встречает это ключевое слово, он автоматически прекращает выполнение функции и передает управление вызывающей стороне.

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

const dosomething = () => {
  return 'test'
}
const result = dosomething() // result === 'test'

Вы можете вернуть только одно значение:

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

Используйте массив:

Целевая аудитория пользователей:

вложенные функции

Функция может быть определена внутри другой функции:

const dosomething = () => {
  const dosomethingelse = () => {}
  dosomethingelse()
  return 'test'
}

Вложенные функции находятся в области действия внешней функции и не могут быть вызваны из других мест.

метод объекта

При использовании в качестве свойств объекта функции вызываются как методы:

const car = {
  brand: 'Ford',
  model: 'Fiesta',
  start: function() {
    console.log(`Started`)
  }
}
car.start()

"это" в стрелочных функциях

В качестве объектных методов важны стрелочные функции и обычные функции "this". Посмотрите этот пример:

const car = {
  brand: 'Ford',
  model: 'Fiesta',
  start: function() {
    console.log(`Started ${this.brand} ${this.model}`)
  },
  stop: () => {
    console.log(`Stopped ${this.brand} ${this.model}`)
  }
}

этоstop()Метод не будет работать, как вы ожидаете.

Это связано с тем, что в двух функциональных стиляхthisРазные. в стрелочной функцииthisуказывает на объемлющий контекст, который в данном случаеwindowОбъект:

использоватьfunction(),thisУказывает на хост-объект.

это означаетСтрелочные функции не подходят для методов объектаи конструктор (конструктор стрелки закончится при вызовеTypeErrorошибка).

IIFE, немедленно выполнить функциональное выражение

IIFE выполняется сразу после объявления:

;(function dosomething() {
  console.log('executed')
})()

Вы можете присвоить результат переменной:

const something = (function dosomething() {
  return 'something'
})()

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

функция подъема

Перед выполнением кода JavaScript изменит его порядок в соответствии с правилами.

Как перемещение функции в верхнюю часть области видимости. Вот почему законно писать так:


Под капотом JavaScript перемещает эту функцию перед оператором вызова в той же области видимости, что и другие функции:

function dosomething() {
  console.log('did something')
}
dosomething()

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

dosomething()
const dosomething = function dosomething() {
  console.log('did something')
}

не будет работать:

Это потому, что внутри становится:

const dosomething
dosomething()
dosomething = function dosomething() {
  console.log('did something')
}

это вletутверждение,varОбъявление также не будет работать, но выдается другая ошибка:

Это потому чтоvarОбъявление поднимается и инициализируется значениемundefined,а такжеconstа такжеletтолько для продвижения.

стрелочная функция

Стрелочные функции — самое важное изменение в ES6/ES2015, и теперь они широко используются. Они отличаются от обычных функций, и давайте посмотрим, почему.

Я рассмотрел функции стрелок выше, но они важны, поэтому я расскажу о них отдельно.

Стрелочные функции были представлены в ES6/ES2015, и их существование навсегда изменило способ написания (и работы) кода JavaScript.

На мой взгляд, это изменение настолько популярно, что редко используется в современном коде.functionключевые слова.

Визуально это долгожданное и легкое изменение, позволяющее написать функцию с более простым синтаксисом, из:

const myFunction = function foo() {
  //...
}

стали:

const myFunction = () => {
  //...
}

Если в теле функции есть только один оператор, вы можете опустить фигурные скобки и написать все в одной строке:

const myFunction = () => doSomething()

Передайте параметры в скобках:

const myFunction = (param1, param2) => doSomething(param1, param2)

Если есть только один параметр, вы можете игнорировать круглые скобки:

const myFunction = param => doSomething(param)

Спасибо за этот синтаксис, стрелочные функцииПоощряйте использование коротких функций.

неявный возврат

Стрелочные функции могут неявно возвращать значения: не используйтеreturnВозврат ключевого слова.

Это работает, когда тело функции имеет только один оператор:

const myFunction = () => 'test'

myFunction() //'test'

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

const myFunction = () => ({value: 'test'})

myFunction() //{value: 'test'}

Как это работает в стрелочных функциях

thisэто сложная концепция для понимания, контекст делает ее другой, и на нее также влияет режим JavaScript (строгий режим или нет).

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

Когда метод в объекте определен, в обычной функцииthisуказывает на этот объект, поэтому вы можете:

const car = {
  model: 'Fiesta',
  manufacturer: 'Ford',
  fullName: function() {
    return `${this.manufacturer} ${this.model}`
  }
}

передачаcar.fullname()вернусьFord Fiesta.

стрелочная функцияthisУнаследовано от контекста выполнения. Стрелочные функции вообще не связываютсяthis, поэтому его значение запрашивается в стеке вызовов, поэтому в этом кодеcar.fullName()бессмысленно, то вернитесьundefined undefined:

const car = {
  model: 'Fiesta',
  manufacturer: 'Ford',
  fullName: () => {
    return `${this.manufacturer} ${this.model}`
  }
}

Из-за этого стрелочные функции не подходят для объектных методов.

Стрелочные функции также нельзя использовать для инициализации конструктора объекта, они выбрасываютTypeError.

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

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

const link = document.querySelector('#link')
link.addEventListener('click', () => {
  // this === window
})

const link = document.querySelector('#link')
link.addEventListener('click', function() {
  // this === link
})

Закрытие

Это дружеское введение в тему замыканий и ключ к пониманию того, как работают функции JavaScript.

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

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

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

Давайте быстро проверим это на примере:

const bark = dog => {
  const say = `${dog} barked!`
  ;(() => console.log(say))()
}

bark(`Roger`)

Это распечатывает, как и ожидалось:Roger barked!.

Если вы хотите вернуться к операции, сделайте следующее:

const prepareBark = dog => {
  const say = `${dog} barked!`
  return () => console.log(say)
}

const bark = prepareBark(`Roger`)

bark()

Этот фрагмент будет распечатанRoger barked!.

Последний пример, создание двух разных собак.prepareBark:

const prepareBark = dog => {
  const say = `${dog} barked!`
  return () => {
    console.log(say)
  }
}

const rogerBark = prepareBark(`Roger`)
const sydBark = prepareBark(`Syd`)

rogerBark()
sydBark()

Распечатать:

Roger barked!
Syd barked!

Как видите, переменнаяsayРезультат и функцияprepareBarkТо, что возвращается, связано.

второй звонокprepareBark()переопределяет новыйsayпеременная, но не повлияет на первуюprepareBark()объем .

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

множество

Поскольку массивы JavaScript имеют все больше и больше возможностей, иногда может быть сложно понять, что использовать и какой метод использовать. Этот раздел призван объяснить, что вы должны использовать с 2018 года.

инициализировать массив

const a = []
const a = [1, 2, 3]
const a = Array.of(1, 2, 3)
const a = Array(6).fill(1) //init an array of 6 items of value 1

Не используйте старый синтаксис (кроме типизированных массивов):

const a = new Array() //never use
const a = new Array(1, 2, 3) //never use

получить длину массива

const l = a.length

пройти черезeveryИтерация

a.every(f)

траверсaдоf()Вернуть ложь.

пройти черезsomeперебирать массив

a.some(f)

траверсaдоf()Вернуть истину.

Перебирает массив и возвращает новый массив результатов функции.

const b = a.map(f)

траверсa, который возвращает каждыйaисполнение элементаf()Результирующий массив результатов.

массив фильтров

const b = a.filter(f)

траверсa, который возвращает каждыйaисполнение элементаf()Новый массив всех правд.

Reduce

a.reduce((accumulator, currentValue, currentIndex, array) => {
  //...
}, initialValue)

reduce()Функция обратного вызова вызывается для каждого элемента массива, и пошагово вычисляется результат вычисления. еслиinitaiValueсуществует,accumulatorРавно этому значению на первой итерации.

пример:


foreach

ES6

a.forEach(f)

траверсaвоплощать в жизньf, не может остановиться на полпути.

пример:

a.forEach(v => {
  console.log(v)
})

for...of

ES6

for (let v of a) {
  console.log(v)
}

for

for (let i = 0; i < a.length; i += 1) {
  //a[i]
}

траверсa, в состоянии пройтиreturnилиbreakВне цикла, поcontinueВырваться из цикла.

@@iterator

ES6

Получить значение итератора массива:

const a = [1, 2, 3]
let it = a[Symbol.iterator]()

console.log(it.next().value) //1
console.log(it.next().value) //2
console.log(it.next().value) //3

.entries()Возвращает итератор пар ключ-значение:

let it = a.entries()

console.log(it.next().value) //[0, 1]
console.log(it.next().value) //[1, 2]
console.log(it.next().value) //[2, 3]

.keys()Вернуть итератор, содержащий все имена ключей:

let it = a.keys()

console.log(it.next().value) //0
console.log(it.next().value) //1
console.log(it.next().value) //2

когда массив заканчивается.next()вернутьundefined. ты можешь пройтиit.next()вернутьvalue, doneЗначение для проверки завершения итерации. при итерации до последнего элементаdoneЗначение всегдаtrue.

Добавить значение в конец массива

a.push(4)

добавить значение в начало массива

a.unshift(0)
a.unshift(-2, -1)

удалить значение из массива

удалить значение в конце

a.pop()

удалить значение в начале

a.shift()

Удалить значение в любом месте

a.splice(0, 2) // get the first 2 items
a.splice(3, 2) // get the  2 items starting from index 3

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

удалить и вставить значения

a.splice(2, 3, 2, 'a', 'b') //removes 3 items starting from
//index 2, and adds 2 items,
// still starting from index 2

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

const a = [1, 2]
const b = [3, 4]
a.concat(b) // 1, 2, 3, 4

Найти определенный элемент в массиве

ES5

a.indexOf()

Возвращает индекс первого совпадающего элемента или -1, если элемент не существует.

a.lastIndexOf()

Возвращает индекс последнего сопоставленного элемента или -1, если элемент не существует.

ES6

a.find((element, index, array) => {
  //return true or false
})

Возвращает первый элемент, соответствующий условию, или неопределенный, если он не существует.

Обычно используется так:

a.find(x => x.id === my_id)

Приведенный выше пример вернет массивid === my_idпервый элемент из .

findIndexВозвращает индекс первого элемента, который соответствует условию, или возвращает, если он не существуетundefined:

a.findIndex((element, index, array) => {
  //return true or false
})

ES7

a.includes(value)

еслиaВключатьvalueВернуть истину.

a.includes(value, i)

еслиaИз местоположенияiвключить послеvalueВернуть истину.

получить часть массива

a.slice()

сортировка массива

Сортировать по алфавиту (по значению ASCII -0-9A-Za-z):

const a = [1, 2, 3, 10, 11]
a.sort() //1, 10, 11, 2, 3

const b = [1, 'a', 'Z', 3, 2, 11]
b = a.sort() //1, 11, 2, 3, Z, a

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

const a = [1, 10, 3, 2, 11]
a.sort((a, b) => a - b) //1, 2, 3, 10, 11

обратный порядок

a.reverse()

массив в строку

a.toString()

Возвращает значение типа строка

a.join()

Возвращает строку, объединенную с элементами массива. Передайте параметр для настройки разделителя:

a.join(',')

скопировать все значения

const b = Array.from(a)
const b = Array.of(...a)

скопировать частичное значение

const b = Array.from(a, x => x % 2 == 0)

скопировать значение себе в другое место

const a = [1, 2, 3, 4]
a.copyWithin(0, 2) // [3, 4, 3, 4]
const b = [1, 2, 3, 4, 5]
b.copyWithin(0, 2) // [3, 4, 5, 4, 5]
//0 is where to start copying into,
// 2 is where to start copying from
const c = [1, 2, 3, 4, 5]
c.copyWithin(0, 2, 4) // [3, 4, 3, 4, 5]
//4  is an end index

цикл

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

for

const list = ['a', 'b', 'c']
for (let i = 0; i < list.length; i++) {
  console.log(list[i]) //value
  console.log(i) //index
}
  • в состоянии пройтиbreakпрерыватьforцикл
  • в состоянии пройтиcontinueпропустить текущийforцикл

forEach

Представлен в ES5. дает вам массив, вы можете передатьlist.forEach()Перебираем его свойства:

const list = ['a', 'b', 'c']
list.forEach((item, index) => {
  console.log(item) //value
  console.log(index) //index
})
//index is optional
list.forEach(item => console.log(item))

К сожалению, вы не можете разорвать эту петлю.

do...while

const list = ['a', 'b', 'c']
let i = 0
do {
  console.log(list[i]) //value
  console.log(i) //index
  i = i + 1
} while (i < list.length)

в состоянии пройтиbreakпрерыватьdo...whileцикл:

do {
  if (something) break
} while (true)

в состоянии пройтиcontinueпропустить текущийdo...whileцикл:

do {
  if (something) continue
  //do something else
} while (true)

while

const list = ['a', 'b', 'c']
let i = 0
while (i < list.length) {
  console.log(list[i]) //value
  console.log(i) //index
  i = i + 1
}

в состоянии пройтиbreakпрерыватьwhileцикл:

while (true) {
  if (something) break
}

в состоянии пройтиcontinueпропустить текущийwhileцикл:

while (true) {
  if (something) continue
  //do something else
}

а такжеdo...whileразница в томdo...whileзациклится хотя бы один раз.

for...in

Перебирает все итерируемые имена свойств объекта.

for (let property in object) {
  console.log(property) //property name
  console.log(object[property]) //property value
}

for...of

Представлено в ES2015for...ofЦиклы, которые сочетают в себе простоту использования и непрерывность forEach:

//iterate over the value
for (const value of ['a', 'b', 'c']) {
  console.log(value) //value
}

//get the index as well, using `entries()`
for (const [index, value] of ['a', 'b', 'c'].entries()) {
  console.log(index) //index
  console.log(value) //value
}

Обратите внимание на использованиеconst. Этот цикл создает новую область видимости на каждой итерации, поэтому мы можем безопасно использовать ее вместо этого.let.

for...in vs for...of

а такжеfor...inразница в следующем:

  • for...ofПеребирать значения свойств
  • for...inИтерация имен свойств

мероприятие

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

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

Есть много разных событий.

обработчик события

ты можешь пройтиобработчик событияРеагировать на все события означает вызывать соответствующую функцию при возникновении события.

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

JavaScript предоставляет три способа регистрации обработчиков событий:

Встроенный процессор событий

Этот метод сейчас редко используется из-за его ограничений, но в раннем JavaScript это был единственный метод:

<a href="site.com" onclick="dosomething();">A link</a>

Обработчик событий DOM

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

window.onload = () => {
  //window loaded
}

Это также распространено при обработке запросов XHR:

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
  //.. do something
}

ты можешь пройтиif ('onsomething' in window) {}Проверьте, назначен ли обработчик свойству.

использовать addEventListener()

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

window.addEventListener('load', () => {
  //window loaded
})

Примечание. IE8 и более ранние версии не поддерживают этот метод, вы можете использоватьattachEvent()заменять. Это важно, если вам нужна совместимость со старыми браузерами.

Слушайте разные элементы

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

Вот почемуaddEventListenerиногда вwindowвызывается для определенного элемента DOM со временем.

объект события

Обработчик события получаетEventобъект в качестве первого аргумента:

const link = document.getElementById('my-link')
link.addEventListener('click', event => {
  // link clicked
})

Этот объект содержит множество полезных свойств и методов, таких как:

  • target, целевой элемент DOM, в котором произошло событие
  • type, тип события
  • stopPropagation(), вызываемый для остановки распространения событий DOM

(Посмотреть полный список)

другие свойства предоставляются для определенных событий,EventПросто интерфейс для разных событий:

Каждый из приведенных выше ссылок ведет на страницу MDN, где вы можете просмотреть все их свойства.

Например, когда происходит событие клавиатуры, вы можете проверить, какая клавиша была нажата пользователем.keyЗначение свойства получает удобочитаемое значение (Escape, Enterтак далее):

window.addEventListener('keydown', event => {
  // key pressed
  console.log(event.key)
})

В событиях мыши мы можем, пока не будет нажата кнопка:

const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
  // mouse button pressed
  console.log(event.button) //0=left, 2=right
})

Всплывающие события и захват событий

Всплывающие события и захват событий — две модели распространения событий.

Предположим, ваша структура DOM выглядит так:

<div id="container">
  <button>Click me</button>
</div>

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

Эти обработчики событий вызываются в порядке, определяемом моделью всплытия/перехвата событий.

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

В нашем случаеbuttonПроцессор на#containerпроизошло раньше.

ловитьКак раз наоборот: самое внешнее событие произойдет перед определенным обработчиком, напримерbutton.

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

Вы также можете использовать захват событий, установив для третьего параметра addEventListener значениеtrue:

document.getElementById('container').addEventListener(
  'click',
  () => {
    //window loaded
  },
  true
)

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

Этот порядок следует следующему принципу: DOM перебирает все элементы, начиная с объекта Window, пока не будет найден объект, по которому щелкнули. При этом вызываются любые связанные обработчики событий (этап захвата). Как только он находит целевой элемент, он повторяет этот процесс до тех пор, пока не вернется к объекту Window, после чего вызывается соответствующий обработчик события (этап всплытия).

предотвратить распространение

События элемента DOM всегда будут распространяться на его родительское дерево, если только оно не заблокировано вручную:

<html>
  <body>
    <section>
      <a id="my-link" ...>

aСобытие click распространяется наsectionПослеbody.

вы можете позвонитьstopPropagation()Метод предотвращает распространение события и обычно размещается в конце обработчика события:

const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
  // process the event
  // ...
  
  event.stopPropagation()
})

общие события

Вот список событий, которые вы будете часто использовать.

load

windowа такжеbodyэлементальloadСобытие срабатывает, когда загрузка страницы завершена.

событие мыши

clickСобытие срабатывает при щелчке мышью.dbclickСобытие срабатывает при двойном щелчке мыши, конечно, в этом случае оно срабатывает первымclickмероприятие.mousedown,mousemoveа такжеmouseupМожно комбинировать с событиями перетаскивания. будьте осторожныmousemove, он срабатывает много раз во время движения мыши (см. далеедросселирование).

событие клавиатуры

keydownСобытие срабатывает при нажатии на клавиатуру (ив нажатом состояниипродолжать запускать).keyupСобытие срабатывает при отпускании клавиатуры.

прокрутить

scrollСобытие срабатывает каждый раз, когда страница прокручивается. Внутри этого обработчика событий вы можете передатьwindow.scrollY(ось Y) Просмотр текущей позиции прокрутки.

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

дросселирование

Как указано выше,mousemoveа такжеscrollЭто не одноразовые события, они вызывают обработчики событий непрерывно в течение всей операции. Это потому, что они должны предоставить координаты, которые вам нужно знать.

Если вы выполняете сложные операции в этих обработчиках событий, это повлияет на производительность и создаст проблемы для ваших веб-пользователей.

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

let cached = null
window.addEventListener('scroll', event => {
  if (!cached) {
    setTimeout(() => {
      //you can access the original event at `cached`
      cached = null
    }, 100)
  }
  cached = event
})

цикл событий

Цикл событий — самая важная вещь в JavaScript.

Я использую JavaScript в течение многих лет, и я неполностьюпонять, как это работает. Конечно, это не имеет значения, если вы не знаете тонкостей, но обычно полезно знать, как это работает, и вам может быть любопытно.

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

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

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

Как правило, большинство браузеров имеют отдельные циклы обработки событий для каждой вкладки просмотра, чтобы изоляция процесса не блокировала весь браузер из-за бесконечных циклов или страниц с интенсивной обработкой. Браузер управляет несколькими одновременными циклами событий для разрешения вызовов API. Веб-воркеры также работают в собственном цикле событий.

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

блокирующий цикл событий

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

Большинство примитивов JavaScript не блокируют, например, сетевые запросы, операции с файловой системой Node.js и многое другое. Блокировка является неожиданной, поэтому JavaScript основан на множестве обратных вызовов, а в последнее время — на promises и async/await.

стек вызовов

Стек вызовов - это очередь Lifo (в последний раз, во-первых).

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

Вы понимаете трассировку стека ошибок в отладчике или консоли браузера? Браузер ищет в стеке вызовов имя функции, а затем отмечает, какая функция инициировала текущий вызов:

Простая иллюстрация цикла событий

Например:

const bar = () => console.log('bar')
const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  bar()
  baz()
}

foo()

Этот код выводит:

foo
bar
baz

как и ожидалось.

Когда этот код запускается, первыйfoo()называется, вfoo()сначала внутренний вызовbar(), а затем позвонитеbaz().

На данный момент наш стек вызовов выглядит так:

Каждая итерация цикла событий проверяет, есть ли что-то еще в стеке вызовов, и выполняет это:

пока весь стек вызовов не станет пустым.

очередь выполнения функций

Приведенный выше пример обычный и ничего особенного: JavaScript находит то, что нужно выполнить, и выполняет его по порядку.

Давайте посмотрим, как отложить выполнение функции до очистки стека вызовов.

использоватьsetTimeout(() => {}, 0)Вызов функции выполнит функцию в тот момент, когда все остальные функции были выполнены.

Например:

const bar = () => console.log('bar')
const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  setTimeout(bar, 0)
  baz()
}

foo()

Результаты удивляют:

foo
baz
bar

Когда этот код запускается,foo()называется первым.foo()Внутри сначала вызывается setTimeout, аbarПередаем таймер в качестве параметра, мы передаем 0, чтобы дать указание на немедленное выполнение, а затем вызываемbaz().

На данный момент стек вызовов выглядит так:

Порядок выполнения всех функций в нашей программе:

почему это так?

очередь сообщений

При вызове setTimeout() браузер или Node.js запускает отсчет времени. В этом примере мы используем 0 в качестве времени задержки, когда время истекло, функция обратного вызова будет помещена вочередь сообщений.

Очередь сообщений также содержит события щелчка или клавиатуры от пользователя или очередь, которая уже существует до вашего кода для получения ответов, или что-то вродеonLoadТакие события DOM.

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

нам не нужно ждать, какsetTimeout, fetch и другие функции, выполняющие свою работу, поскольку они предоставляются браузером, имеют собственный поток. Например, если вы установите задержку в 2 секундыsetTimeoutС таймером вам не нужно ждать 2 секунды — ожидание выполняется в другом месте.

Очередь работ ES6 (Очередь заданий)

ECMASciprt 2015 представил концепцию рабочих очередей, используемую в промисах (также представленную в ES6/ES2015). Это способ выполнить асинхронную функцию как можно быстрее, а не в конце стека вызовов.

Завершено до окончания текущей функции Промисы будут выполнены после текущей функции.

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

пример:

const bar = () => console.log('bar')
const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  setTimeout(bar, 0)
  new Promise((resolve, reject) =>
    resolve('should be right after baz, before bar')
  ).then(resolve => console.log(resolve))
  baz()
}

foo()

Это печатает:

foo
baz
should be right after baz, before bar
bar

Вот промисы (включая Async/await на основе промисов) и нативные старые асинхронные функции.setTimeout()Или самая большая разница между API других платформ.

Асинхронное программирование и обратные вызовы

JavaScript по умолчанию является асинхронным и однопоточным. Это означает, что код не может создавать новые потоки и выполняться параллельно. Давайте разберемся, что такое асинхронный код.

Асинхронность в языках программирования

Компьютеры асинхронны по своей конструкции.

Асинхронность — это когда что-то может происходить независимо от основного потока программы.

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

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

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

В общем, языки программирования являются асинхронными, и некоторые из них также предоставляют способ асинхронной работы.Эти языки или библиотеки, C, Java, C#, PHP, Go, Ruby, Swift, Python по умолчанию являются асинхронными, и некоторые из них используют потоки.Обрабатывайте асинхронность, порождайте новый процесс.

JavaScript

JavaScript по умолчанию является асинхронным и однопоточным. Это означает, что код не может создавать новые потоки и выполняться параллельно.

Каждая строка кода выполняется одна за другой, например:

const a = 1
const b = 2
const c = a * b
console.log(c)
doSomething()

Но JavaScript был создан для браузеров, и его основная задача в начале заключалась в том, чтобы реагировать на действия пользователя, такие какonClick,onMouseOver,onChnage,onSubmitи т.п. Как он может использовать шаблон асинхронного программирования?

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

Особенно недавно Node.js представил неблокирующую среду ввода-вывода, чтобы расширить эту концепцию на доступ к файлам, сетевые вызовы и т. д.

Перезвоните

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

document.getElementById('button').addEventListener('click', () => {
  //item clicked
})

это также называетсяПерезвоните.

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

оберните весь свой код вwindowна объектеloadОбычно используются прослушиватели событий, поэтому код будет запускаться только тогда, когда страница будет готова:

window.addEventListener('load', () => {
  //window loaded
  //do what you want
})

Обратные вызовы используются везде, а не только в событиях DOM.

Типичным примером является использование таймера:

setTimeout(() => {
  // runs after 2 seconds
}, 2000)

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

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4) {
    xhr.status === 200 ? console.log(xhr.responseText) : console.error('error')
  }
}
xhr.open('GET', 'https://yoursite.com')
xhr.send()

Обработка ошибок в обратных вызовах

Как вы обрабатываете ошибки обратного вызова? Распространенной стратегией является использование подхода Node.js: первым параметром функции обратного вызова всегда является объект ошибки:Обратный вызов с приоритетом ошибки.

Если ошибок нет, этот объектnull. Если возникает ошибка, он содержит содержимое с описанием ошибки и другую информацию.

fs.readFile('/file.json', (err, data) => {
  if (err !== null) {
    //handle error
    console.log(err)
    return
  }
  //no errors, process data
  console.log(data)
})

Проблемы с обратными вызовами

Обратные вызовы упрощают простой код!

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

window.addEventListener('load', () => {
  document.getElementById('button').addEventListener('click', () => {
    setTimeout(() => {
      items.forEach(item => {
        //your code here
      })
    }, 2000)
  })
})

Это простой четырехуровневый код, но я вижу много вложений, что неинтересно.

Как решить эту проблему?

Альтернативы обратным вызовам

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

  • Обещания (ES6)
  • Асинхронное/ожидание (ES8)

Promises

Промисы — это способ для JavaScript решить проблему написания слишком большого количества функций обратного вызова в асинхронном коде.

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

Промисы — это способ решения асинхронного кода без написания слишком большого количества функций обратного вызова в коде. Хотя с годами он стал популярным, он также был представлен и стал стандартным в ES2015, а в ES2017 был заменен асинхронными функциями.

Как работают обещания (короткометражный)

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

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

Какие JS API используют промисы?

В дополнение к вашему собственному коду и некоторым библиотекам веб-API также используют промисы:

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

создать обещание

Promise API предоставляет конструктор Promise, который вы можете передатьnew Promise()инициализация:

let done = true

const isItDoneYet = new Promise(
  (resolve, reject) => {
    if (done) {
      const workDone = 'Here is the thing I built'
      resolve(workDone)
    } else {
      const why = 'Still working on something else'
      reject(why)
    }
  }
)

Вы можете увидеть это обещание в глобальной константеdoneКогда true, возвращает разрешенное обещание, в противном случае возвращает отклоненное обещание.

использоватьresolveа такжеrejectМы можем вернуть значение, в приведенном выше примере мы вернули строку, или мы можем вернуть объект.

использовать промисы

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

Теперь давайте посмотрим, какиспользоватьобещать.

const isItDoneYet = new Promise(
  //...
)

const checkIfItsDone = () => {
  isItDoneYet
    .then((ok) => {
      console.log(ok)
    })
    .catch((err) => {
      console.error(err)
    })
}

бегатьcheckIfItsDone()будет выполнятьisItDoneYet()обещание и подождите, пока он разрешит вызовthenобратный вызов, если произойдет ошибка, он будет вызван вcatchОбработка ошибок в обратном вызове.

скованные обещания

Промис может вернуть другой промис, образуя цепочку промисов.

Хороший пример — Fecth API (верхний уровень API XMLHttpRequest). Мы можем использовать его, чтобы получить ресурсы и связать их обещаниями.

Fetch API основан на механизме промисов, вызываяfetch()эквивалентно нашему прохождениюnew promise()Дайте определение обещанию.

Пример связанного обещания

const status = (response) => {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  }
  return Promise.reject(new Error(response.statusText))
}

const json = (response) => response.json()
fetch('/todos.json')
  .then(status)
  .then(json)
  .then((data) => { console.log('Request succeeded with JSON response', data) })
  .catch((error) => { console.log('Request failed', error) })

В этом примере мы называемfetch()из корневого доменаtodos.jsonФайл получает список TODO, и мы создаем цепочку обещаний.

бегатьfetch()возвращает массив со многими свойствамиотклик, которую мы цитируем:

  • status, значение, отражающее статус HTTP
  • statusText, сообщение о состоянии, когда запрос выполнен успешноOK

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

Исходя из этого: первый промис в цепочке — это функция, которую мы определилиstatus(), который используется для проверки статуса ответа и отклонения обещания, если результат не находится между 200 и 299. Это приведет к тому, что цепочка обещаний пропустит все связанные списки и перейдет непосредственно к концу цепочки.catch()заявление, распечататьRequest failedи сообщения об ошибках.

В случае успеха он вызовет наш определенныйjson()функция. В случае успеха возвращается предыдущее обещаниеresponseobject в качестве входных данных для второго обещания.

В процессе мы возвращаем обработанные данные JSON, поэтому третий промис получает объект JSON напрямую:

.then((data) => {
  console.log('Request succeeded with JSON response', data)
})

Они будут напечатаны на консоли.

обрабатывать ошибки

В приведенном выше примере за цепочкой обещаний следуетcatchКусок. При возникновении какой-либо ошибки в связанных промисах или отказе в ручном возврате управление программой будет передано ближайшему после кода ошибки.catch()утверждение.

new Promise((resolve, reject) => {
  throw new Error('Error')
})
  .catch((err) => { console.error(err) })

// or

new Promise((resolve, reject) => {
  reject('Error')
})
  .catch((err) => { console.error(err) })

каскадные ошибки

если вcatch()внутренне выдает еще одну ошибку, вы можете добавить вторуюcatch()разобраться с этим и так далее.

new Promise((resolve, reject) => {
  throw new Error('Error')
})
  .catch((err) => { throw new Error('Error') })
  .catch((err) => { console.error(err) })

Оркестрировать обещания

Promise.all()

Если вам нужно обрабатывать несколько промисов одновременно,Promise.all()Может помочь вам определить набор обещаний и дождаться их завершения, прежде чем что-то делать. Например:

const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')

Promise.all([f1, f2]).then((res) => {
    console.log('Array of results', res)
})
.catch((err) => {
  console.error(err)
})

Структурный синтаксис ES2015 также позволяет вам сделать это:

Promise.all([f1, f2]).then(([res1, res2]) => {
    console.log('Results', res1, res2)
})

Это не ограничиваетсяfetch,Любое обещание может быть обработано.

Promise.race()

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

const first = new Promise((resolve, reject) => {
    setTimeout(resolve, 500, 'first')
})
const second = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'second')
})

Promise.race([first, second]).then((result) => {
  console.log(result) // second
})

Асинхронно и ждать

Теперь мы рассмотрим более современный подход к асинхронным функциям в JavaScript. Потребовалось совсем немного времени, чтобы JavaScript превратился из callback-функций в Promises, а начиная с ES2017 асинхронность и ожидание значительно упростили асинхронный JavaScript.

Асинхронные функции сочетают в себе промисы и генераторы и в основном могут рассматриваться как абстрактные методы поверх промисов. Я повторяю:async и await основаны на промисах.

Почему введение Async и ждут?

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

Когда ES2015 представил промисы, это было просто для решения проблемы асинхронного кода, что было хорошо сделано, но за два года между ES2015 и ES2017 люди обнаружили, чтообещания не являются окончательным решением.

Обещания были введены для решения знаменитойад обратного звонка

const doSomethingAsync = () => {
    return new Promise((resolve) => {
        setTimeout(() => resolve('I did something'), 3000)
    })
}

awaitasync

const doSomething = async () => {
    console.log(await doSomethingAsync())
}

const doSomethingAsync = () => {
    return new Promise((resolve) => {
        setTimeout(() => resolve('I did something'), 3000)
    })
}

const doSomething = async () => {
    console.log(await doSomethingAsync())
}

console.log('Before')
doSomething()
console.log('After')

Before
After
I did something //after 3s

async

const aFunction = async () => {
  return 'test'
}

aFunction().then(alert) // This will alert 'test'

const aFunction = async () => {
  return Promise.resolve('test')
}

aFunction().then(alert) // This will alert 'test'

const getFirstUserData = () => {
  return fetch('/users.json') // get users list
    .then(response => response.json()) // parse JSON
    .then(users => users[0]) // pick first user
    .then(user => fetch(`/users/${user.name}`)) // get user data
    .then(userResponse => response.json()) // parse JSON
}

getFirstUserData()

const getFirstUserData = async () => {
  const response = await fetch('/users.json') // get users list
  const users = await response.json() // parse JSON
  const user = users[0] // pick first user
  const userResponse = await fetch(`/users/${user.name}`) // get user data
  const userData = await user.json() // parse JSON
  return userData
}

getFirstUserData()

const promiseToDoSomething = () => {
    return new Promise(resolve => {
        setTimeout(() => resolve('I did something'), 10000)
    })
}
const watchOverSomeoneDoingSomething = async () => {
    const something = await promiseToDoSomething()
    return something + ' and I watched'
}
const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {
    const something = await watchOverSomeoneDoingSomething()
    return something + ' and I watched as well'
}

watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => {
    console.log(res)
})

I did something and I watched and I watched as well

const operations = []

for (var i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}

i

0
1
2
3
4

5
5
5
5
5

var

var

var i;
const operations = []

for (i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}

ii

letvar

varlet

const operations = []

for (let i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}

0
1
2
3
4

ioperationsi

i

const operations = []

for (var i = 0; i < 5; i++) {
  operations.push(((j) => {
    return () => console.log(j)
  })(i))
}

for (const operation of operations) {
  operation()
}

При написании кода JavaScript вы можете захотеть отложить выполнение функции. В этом разделе мы обсудим, как использовать setTimeout и setInterval для планирования будущих функций.

setTimeout()

При написании кода JavaScript вы можете захотеть отложить выполнение функции. Эта работа передаетсяsetTimeout. Вы можете указать функцию для задержки выполнения и желаемое время задержки в миллисекундах:

setTimeout(() => {
  // runs after 2 seconds
}, 2000)

setTimeout(() => {
  // runs after 50 milliseconds
}, 50)

Этот синтаксис определяет новую функцию. Вы можете вызывать ее где угодно, вы также можете передать существующее имя функции или задать некоторые параметры:

const myFunction = (firstParam, secondParam) => {
  // do something
}

// runs after 2 seconds
setTimeout(myFunction, 2000, firstParam, secondParam)

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

const id = setTimeout(() => {
  // should run after 2 seconds
}, 2000)

// I changed my mind
clearTimeout(id)

Нулевая задержка

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

setTimeout(() => {
  console.log('after ')
}, 0)

console.log(' before ')

распечатаетbefore after.

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

Некоторые браузеры (IE и Edge) реализуютsetImmediate()Метод достигает вышеуказанной цели, но не стал стандартом.Не работает в других браузерах. Но доступно в Node.js.

setInterval()

setIntervalа такжеsetTimeoutТочно так же разница заключается в том, что вместо того, чтобы запускать функцию обратного вызова только один раз, она может запускать ее постоянно в течение заданного интервала времени (в миллисекундах):

setInterval(() => {
  // runs every 2 seconds
}, 2000)

Вышеупомянутая функция запускается каждые две секунды, если вы не используетеclearIntervalпрекрати, проходиsetIntervalВозвращаемый идентификатор интервала:

const id = setInterval(() => {
  // runs every 2 seconds
}, 2000)

clearInterval(id)

Вызывается в функции обратного вызова setIntervalclearIntervalОчень часто это оставляет за ним право решать, нужно ли ему продолжать работу. Код в примере будет выполняться всегда, если только значение App.somethingIWait не равноarrived:

const interval = setInterval(() => {
  if (App.somethingIWait === 'arrived') {
    clearInterval(interval)
    return
  }
  // otherwise do things
}, 100)

рекурсивный setTimeout

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

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

Также время выполнения совпадает со следующим:

Чтобы избежать этого, вы можете использовать рекурсивный setTImeout, вызываемый после завершения функции обратного вызова:

const myFunction = () => {
  // do something
  setTimeout(myFunction, 1000)
}

setTimeout(
  myFunction()
}, 1000)

Реализуйте эту ситуацию:

setTimeoutа такжеsetIntervalДоступно в модуле Таймеры Node.js.

Node.js также предоставляетsetImmediate(), и использоватьsetTimeout(() => {}, 0)Эффект тот же, в основном используется в цикле событий Node.js.

This

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

это в строгом режиме

строгий режим, вне объектаthisвсегдаundefined.

Обратите внимание, я упомянул строгий режим. Если строгий режим не включен (по умолчанию выключен, если вы явно не добавилиuse strict), неаккуратный режим (sloppy mode), никаких специальных указаний, упомянутых нижеthisОба указывают на глобальный объект. В среде браузера естьwindowобъект.

это в методе

Методы — это функции, прикрепленные к объектам.

Вы можете увидеть различные форматы, такие как:

const car = {
  maker: 'Ford',
  model: 'Fiesta',
  drive() {
    console.log(`Driving a ${this.maker} ${this.model} car!`)
  }
}

car.drive()
//Driving a Ford Fiesta car!

В этом примере используется обычная функция,thisАвтоматически привязывается к этому объекту.

Примечание: приведенное выше объявление метода иdrive: function() {..}То же самое, только короче:

const car = {
  maker: 'Ford',
  model: 'Fiesta',
  
  drive: function() {
    console.log(`Driving a ${this.maker} ${this.model} car!`)
  }
}

То же самое и в этом примере:

const car = {
  maker: 'Ford',
  model: 'Fiesta'
}

car.drive = function() {
  console.log(`Driving a ${this.maker} ${this.model} car!`)
}

car.drive()
//Driving a Ford Fiesta car!

Стрелочные функции отличаются тем, что они связаны лексически:

const car = {
  maker: 'Ford',
  model: 'Fiesta',
  
  drive: () => {
    console.log(`Driving a ${this.maker} ${this.model} car!`)
  }
}

car.drive()
//Driving a undefined undefined car!

связать функцию стрелки

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

Явно передать объект, на который указывает this

JavaScript обеспечивает сопоставлениеthisи методы объектов.

существуетобъявление функциисценическое использованиеbind():

const car = {
  maker: 'Ford',
  model: 'Fiesta'
}

const drive = function() {
  console.log(`Driving a ${this.maker} ${this.model} car!`)
}.bind(car)

drive()
//Driving a Ford Fiesta car!

Вы также можете переназначить уже существующий объект какthisстоимость:

const car = {
  maker: 'Ford',
  model: 'Fiesta',
  drive() {
    console.log(`Driving a ${this.maker} ${this.model} car!`)
  }
}

const anotherCar = {
  maker: 'Audi',
  model: 'A4'
}

car.drive.bind(anotherCar)()
//Driving a Audi A4 car!

существуетвызов функциисценическое использованиеcall()а такжеapply():

const car = {
  maker: 'Ford',
  model: 'Fiesta'
}

const drive = function(kmh) {
  console.log(`Driving a ${this.maker} ${this.model} car at ${kmh} km/h!`)
}

drive.call(car, 100)
//Driving a Ford Fiesta car at 100 km/h!

drive.apply(car, [100])
//Driving a Ford Fiesta car at 100 km/h!

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

Особые случаи в обработке событий браузера

в обратном вызове обработчика события.thisУкажите на элемент HTML, который получил событие:

document.querySelector('#button').addEventListener('click', function(e) {
  console.log(this) //HTMLElement
})

Вы можете связать так:

document.querySelector('#button').addEventListener(
  'click',
  function(e) {
    console.log(this) //Window if global, or your context
  }.bind(this)
)

строгий режим

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

Строгий режим в основном удаляет функции, которые присутствовали в ES3, которые устарели, начиная с ES5 (не удалены для нужд обратной совместимости).

Как включить строгий режим

Строгий режим является необязательным. С несовместимыми изменениями мы не можем просто изменить поведение языка по умолчанию, это нарушило бы большую часть кода JavaScript, а JavaScript приложил немало усилий, чтобы гарантировать, что код 1996 года по-прежнему работает сегодня. Это также является залогом его успеха.

Поэтому, когда нам нужно включить строгий режим, у нас естьuse strictинструкция. Вы можете поместить это в начало файла, чтобы применить его ко всему файлу:

'use strict'

const name = 'Flavio'
const hello = () => 'hey'

//...

Вы также можете включить строгий режим в отдельной функции, просто поместитеuse strictПоместите его в начало тела функции:

function hello() {
  'use strict'
  
  return 'hey'
}

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

Изменения в строгом режиме

неожиданная глобальная переменная

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

;(function() {
  variable = 'hey'
})()(() => {
  name = 'Flavio'
})()

variable //'hey'
name //'Flavio'

Включите строгий режим, это приведет к ошибке:

;(function() {
  'use strict'
  variable = 'hey'
})()(() => {
  'use strict'
  myname = 'Flavio'
})()

ошибка распределения

JavaScript незаметно обрабатывает некоторые ошибки преобразования.

В строгом режиме отображаются следующие ошибки:

const undefined = 1(() => {
  'use strict'
  undefined = 1
})()

бесконечность,eval,argumentsИ так по тому же.

В JavaScript вы можете определить недоступное для записи свойство объекта, например:

const car = {}
Object.defineProperty(car, 'color', { value: 'blue', writable: false })

В строгом режиме вы не можете переопределить это значение, но в небрежном режиме вы можете:

Тот же принцип, что и у геттеров:


Нерасширяемый объект можно расширить в неаккуратном режиме:

const car = { color: 'blue' }
Object.preventExtensions(car)

car.model = 'Fiesta'(
  //ok
  () => {
    'use strict'
    car.owner = 'Flavio' //TypeError: Cannot add property owner, object is not extensible
  }
)()

И вы можете установить свойства исходного значения без каких-либо сообщений об ошибках, но это не вступит в силу:

true.false = ''(
  //''
  1
).name =
  'xxx' //'xxx'
var test = 'test' //undefined
test.testing = true //true
test.testing //undefined

Ни один из них не разрешен в строгом режиме:

;(() => {
  'use strict'
  true.false = ''(
    //TypeError: Cannot create property 'false' on boolean 'true'
    1
  ).name =
    'xxx' //TypeError: Cannot create property 'name' on number '1'
  'test'.testing = true //TypeError: Cannot create property 'testing' on string 'test'
})()

удалить ошибку

В поспешном режиме при попытке удалить значение свойства удалить нельзя, JavaScript возвращает только false, а в строгом режиме выдает TypeError:

delete Object.prototype(
  //false
  
  () => {
    'use strict'
    delete Object.prototype //TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
  }
)()

одноименный параметр функции

В общих функциях могут быть конфликтующие имена параметров:

(function(a, a, b) {
  console.log(a, b)
})(1, 2, 3)
//2 3

(function(a, a, b) {
  'use strict'
  console.log(a, b)
})(1, 2, 3)
//Uncaught SyntaxError: Duplicate parameter name not allowed in this context

В этом примере функция стрелки всегда выдаетSyntaxError:

((a, a, b) => {
  console.log(a, b)
})(1, 2, 3)
//Uncaught SyntaxError: Duplicate parameter name not allowed in this context

восьмеричный

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

(() => {
  console.log(010)
})()
//8

(() => {
  'use strict'
  console.log(010)
})()
//Uncaught SyntaxError: Octal literals are not allowed in strict mode.

ты еще можешь пройти0oXXСинтаксис использует восьмеричные числа в строгом режиме:

;(() => {
  'use strict'
  console.log(0o10)
})()
//8

удалено с

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

Функция немедленного выполнения (IIFE)

Немедленные функции выполняются, как только они созданы.

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

Синтаксис для выполнения функции такой:

;(function() {
  /* */
})()

Немедленно выполняющиеся функции также могут использовать стрелочные функции:

;(() => {
  /* */
})()

По сути, мы определяем функцию в круглых скобках, а затем добавляем пару()Выполните эту функцию:(/* function */)().

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

Для объявлений функций требуется имя, а для выражений функций — нет.

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

(function() {
  /* */
}())

(() => {
  /* */
}())

Используйте унарные операторы

Используйте при использовании IIFEЛюбыеУнарные операторы странные, но очень полезные на практике:

;-(function() {
  /* */
})()

+(function() {
 /* */
})()

~(function() {
  /* */
})()

!(function() {
  /* */
})()

(недействительно для стрелочных функций)

назван IIFE

IIFE также могут называть обычные функции (не стрелочные). Это не приводит к тому, что функция «просачивается» в глобальную область видимости, и ее нельзя вызвать снова после ее выполнения:

;(function doSomething() {
  /* */
})()

точка с запятой перед IIFE

ты увидишь:

;(function() {
  /* */
})()

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

Эту проблему можно решить с помощью «умного» инструмента упаковки кода, такого какwebpack.

математический оператор

Это обычное дело для любого языка программирования. JavaScript предоставляет несколько операторов, помогающих нам работать с числами.

арифметические операторы

плюс (+)

const three = 1 + 2
const four = three + 1

+Операторы также объединяют строки, поэтому обратите внимание:

const three = 1 + 2
three + 1 // 4
'three' + 1 // three1

уменьшать(-)

const two = 4 - 2

Удалить(/)

Верните частное между первым числом и вторым числом:

const result = 20 / 5 //result === 4
const result = 20 / 7 //result === 2.857142857142857

Если вы разделите на 0, JavaScript не выдаст никакой ошибки, а вернетInfinity(если он отрицательный, вернуть-Infinity).

1 / 0 //Infinity
-1 / 0 //-Infinity

остаток (%)

Остаток полезен во многих ситуациях:

const result = 20 % 5 //result === 0
const result = 20 % 7 //result === 6

Модуль 0 всегдаNaN, что означает «не число»:

1 % 0 //NaN
-1 % 0 //NaN

брать(*)

1 * 2 //2
-1 * 2 //-2

возведение в степень (**)

Умножьте первый операнд на второй операнд количество раз:

1 ** 2 //1
2 ** 1 //2
2 ** 2 //4
2 ** 8 //256
8 ** 2 //64

унарный оператор

Приращение (++)

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

let x = 0
x++ //0
x //1
++x //2

уменьшение (--)

Аналогичен оператору приращения, но уменьшает значение.

let x = 0
x-- //0
x //-1
--x //-2

Унарный минус (-)

вернуть отрицательное значение

let x = 2
-x //-2
x //2

Унарный Плюс (+)

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

let x = 2
+x //2

x = '2'
+x //2

x = '2a'
+x //NaN

быстрое назначение

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

Они есть:

  • +=: Аддитивное назначение
  • -=: назначение деления
  • *=: Назначение умножения
  • /=: назначение деления
  • %=: взять оставшееся задание
  • **=: присвоение возведения в степень

пример:

const a = 0
a += 5 //a === 5
a -= 2 //a === 3
a *= 2 //a === 6
a /= 2 //a === 3
a %= 2 //a === 1

приоритет

Каждый сложный оператор вводит проблемы приоритета. посмотри на это:

const a = 1 * 2 + 5 / 2 % 2

Результат равен 2,5. Но почему? Какая операция выполняется первой, а какая позже?

Некоторые операторы имеют более высокий приоритет, чем другие. Его приоритет подчиняется следующим правилам:

  • - + ++ --Унарные операторы, инкремент, декремент
  • * / %Умножение/Деление
  • + -Сложение/вычитание
  • = += -= *= /= %= **=оператор присваивания

Родственные операторы (такие как+а также-) выполняются по порядку.

Согласно приведенной выше последовательности, мы можем рассчитать эту формулу:

const a = 1 * 2 + 5 / 2 % 2
const a = 1 * 2 + 5 / 2 % 2
const a = 2 + 2.5 % 2
const a = 2 + 0.5
const a = 2.5

Математический объект

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

постоянный

функция

Все методы функций являются статическими, и Math не может быть унаследован.

Math.abs()

Возвращает абсолютное значение числа

Math.abs(2.5) //2.5
Math.abs(-2.5) //2.5

Math.acos()

Возвращает арккосинус, аргумент должен быть между -1 и 1.

Math.acos(0.8) //0.6435011087932843

Math.asin()

Возвращает арксинус, аргумент должен быть между -1 и 1.

Math.asin(0.8) //0.9272952180016123

Math.atan()

Возвращает арктангенс

Math.atan(30) //1.5374753309166493

Math.atan2()

Возвращает арктангенс отношения своего аргумента

Math.atan2(30, 20) //0.982793723247329

Math.ceil()

Округлен

Math.ceil(2.5) //3
Math.ceil(2) //2
Math.ceil(2.1) //3
Math.ceil(2.99999) //3

Math.cos()

Косинус угла в радианах

Math.cos(0) //1
Math.cos(Math.PI) //-1

Math.exp()

Возвращает Math.E, возведенный в степень аргумента

Math.exp(1) //2.718281828459045
Math.exp(2) //7.38905609893065
Math.exp(5) //148.4131591025766

Math.floor()

округлить

Math.floor(2.5) //2
Math.floor(2) //2
Math.floor(2.1) //2
Math.floor(2.99999) //2

Math.log()

возвращает натуральный логарифм по основанию e

Math.log(10) //2.302585092994046
Math.log(Math.E) //1

Math.max()

Возвращает максимальное значение переданной серии чисел

Math.max(1,2,3,4,5) //5
Math.max(1) //1

Math.min()

Возвращает наименьшее значение в переданной серии чисел

Math.max(1,2,3,4,5) //1
Math.max(1) //1

Math.pow()

Возвращает первый аргумент, возведенный в степень второго аргумента.

Math.pow(1, 2) //1
Math.pow(2, 1) //2
Math.pow(2, 2) //4
Math.pow(2, 4) //16

Math.random()

Возвращает псевдослучайное значение от 0,0 до 1,0.

Math.random() //0.9318168241227056
Math.random() //0.35268950194094395

Math.round()

округление

Math.round(1.2) //1
Math.round(1.6) //2

Math.sin()

Вычислить синус угла в радианах

Math.sin(0) //0
Math.sin(Math.PI) //1.2246467991473532e-16)

Math.sqrt()

Предписание

Math.sqrt(4) //2
Math.sqrt(16) //4
Math.sqrt(5) //2.23606797749979

Math.tan()

Вычислить тангенс угла в радианах

Math.tan(0) //0
Math.tan(Math.PI) //-1.2246467991473532e-16

модуль ЕС

Модули ES — это стандарт ECMAScript для работы с модулями. Node.js уже давно использует CommonJS, однако в браузерах еще нет модульной системы. Каждое серьезное решение (например, модульная система) должно быть сначала стандартизировано ECMAScript, а затем реализовано браузерами.

Этот процесс стандартизации был выполнен в ES6, и в то же время браузеры начали внедрять стандарт постепенно, пытаясь сохранить согласованность. Модули ES теперь поддерживаются Chrome, Safari, Edge и Firefox (начиная с версии 60).

Модули — это круто, они позволяют инкапсулировать произвольную функциональность и предоставлять ее как библиотеку другим файлам JavaScript.

Синтаксис модуля ES

Чтобы импортировать модуль, вы можете использовать:

import package from 'module-name'

Однако CommonJS использует:

const package = require('module-name')

Модуль — этоexport экспортФайл JavaScript с одним или несколькими значениями (объектами, функциями или переменными). Например, этот модуль экспортирует функцию, которая пишет строки с заглавной буквы:

uppercase.js

export default str => str.toUpperCase()

В этом примере модуль определяет уникальный,экспорт по умолчанию, так что это может быть анонимная функция. В противном случае ему нужно имя, чтобы отличать другие экспорты. Сейчас,любой другой модуль JavaScriptЭту функцию можно импортировать, импортировав файл uppercase.js.

Доступ к HTML-странице можно получить через<script>спец на этикеткеtype="module"свойства добавить модуль:

<script type="module" src="index.js"></script>

Примечание. Поведение модуля при импорте иdeferЗагрузка скрипта аналогична. ПроверитьЭффективно загружайте JavaScript с помощью отложенной и асинхронной загрузки.

Важно, чтобы любойtype="module"Модули загружаются в строгом режиме.

В этом примереuppercase.jsмодуль определяетЭкспорт по умолчанию, поэтому мы можем импортировать этот модуль и связать его с именем, которое нам нравится:

import toUpperCase from './uppercase.js'

Тогда мы можем использовать его:

toUpperCase('test') //'TEST'

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

import toUpperCase from 'https://flavio-es-modules-example.glitch.me/uppercase.js'

Этот синтаксис также допустим:

import { foo } from '/uppercase.js'
import { foo } from '../uppercase.js'

Это не работает:

import { foo } from 'uppercase.js'
import { foo } from 'utils/uppercase.js'

Путь должен быть абсолютным или иметь префикс имени./или/.

Другие методы импорта/экспорта

Мы уже знаем этот пример:

export default str => str.toUpperCase()

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

const a = 1
const b = 2
const c = 3

export { a, b, c }

Другой модуль может импортировать все:

import * from 'module'

Вы можете импортировать только часть контента, используя деструктурирующие привязки:

import { a } from 'module'
import { a, b } from 'module'

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

import React, { Component } from 'react'

существуетздесьПосмотрите пример модуля ES.

Совместное использование ресурсов между источниками CORS

пройти черезCORSПолучение модулей означает, что если вы импортируете скрипты из других доменов, они должны иметь действительный заголовок CORS, чтобы разрешить загрузку между страницами (например,Access-Control-Allow-Origin).

А как насчет браузеров, которые не поддерживают модули?

В сочетании сtype=moduleа такжеnomodule:

<script type="module" src="module.js"></script>
<script nomodule src="fallback.js"></script>

Суммировать

Модули ES — самая важная функция, представленная современными браузерами. Они являются частью ES6, но процесс их реализации был долгим.

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

Даже если модуль ES уже зарегистрирован на стороне браузера,Webpackтакже будет играть важную роль, но создание такой функциональности непосредственно в языке было бы обременительным для унификации того, как модули работают на клиенте и Node.js.

CommonJS

Спецификация модулей CommonJS является стандартом для модулей в Node.js.

Браузерный JavaScript использует модули ES.

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

Огромная экосистема npm построена на формате CommonJS.

Синтаксис импорта модуля следующий:

const package = require('module-name')

В CommonJS модули загружаются синхронно и выполняются в том порядке, в котором их находит среда выполнения JavaScript. Эта система изначально создавалась для серверного JavaScript, а не для клиентского (поэтому и были введены модули ES).

Файл JavaScript — это модуль, который экспортирует один или несколько определенных в нем символов, которые могут быть переменными, функциями, объектами:

uppercase.js

exports.uppercase = str => str.toUpperCase()

Любой файл JavaScript может импортировать и использовать этот модуль:

const uppercaseModule = require('uppercase.js')
uppercaseModule.uppercase('test')

существуетGlitchПростой пример выше.

Вы можете экспортировать несколько значений:

exports.a = 1
exports.b = 2
exports.c = 3

Импортируйте каждое значение отдельно с помощью назначения деструктурирования:

const { a, b, c } = require('./uppercase.js')

Или экспортировать только одно значение:

//file.js
module.exports = value

Затем импортируйте:

const value = require('./file.js')

Глоссарий

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

асинхронный

Асинхронный код — это когда вы что-то запускаете, об этом можно забыть, не нужно ждать, а когда результат будет готов, вы его получите автоматически. Типичным примером является вызов AJAX, который может занять много секунд, пока вы можете заниматься другими делами, а когда ответ будет получен, будет вызвана функция обратного вызова. Promises и async/await — более современные способы работы с асинхронным кодом.

область действия блока

Любая переменная, определенная внутри блока на уровне блока, видна всему блоку, и к ней можно получить доступ внутри, но не за пределами блока.

Перезвоните

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

утверждение

Декларативный подход заключается в том, что вы указываете детали, сообщая машине, что вам нужно сделать. React считается декларативным на том основании, что он больше фокусируется на абстрактных рассуждениях, чем на непосредственном редактировании DOM. Каждый язык программирования высокого уровня более декларативен, чем язык программирования низкого уровня, такой как ассемблер. JavaScript более декларативен, чем C, HTML тоже декларативен.

вернитесь

Резервные варианты используются для улучшения работы, когда пользователи не могут получить доступ к определенным функциям. Например, если в браузере отключен JavaScript, пользователь должен получить нативную версию HTML-страницы. Если вы столкнулись с API, который браузер не реализует, у вас должен быть надежный способ избежать серьезного влияния на работу пользователя.

объем функции

Любая переменная, определенная внутри функции в области действия функции, видна всей функции и может быть доступна внутри функции, но не вне функции.

последовательность

Переменная, значение которой нельзя изменить после ее создания, называется неизменной. Изменяемые переменные изменяемы. То же самое в массивах и объектах.

лексический объем

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

Polyfill

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

чистая функция

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

переназначать

varа такжеletПозволяет переназначать переменные бесконечное количество раз.constобъявляет неизменяемую строку, int, boolean, object (но вы все равно можете изменить ее с помощью предоставленных методов).

Объем

Область видимости — это часть набора переменных, которая видна программе.

объем

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

Shim

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

побочный эффект

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

состояние

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

состояние

Компонент, функция или класс с состоянием управляют своим собственным состоянием (данными). Он может хранить массив, таймер или что-то еще.

лица без гражданства

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

строгий режим

Строгий режим — это новая функция ECMAScipt 5.1, она позволит выявлять больше ошибок во время выполнения JavaScript, но может помочь улучшить ваш код JavaScript, отказываясь от других часто упускаемых из виду свойств объектов, необъявленных переменных, конфликтов и т. д. Рекомендация: используйте строгий режим. Другой "режим спешки", чтобы увидеть имя, чтобы знать, что это не очень хорошо.

Tree Shaking

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

Спасибо за прочтение!

Уведомление: Вы можете получить это полное руководство по JavaScriptMarkdown, ePub, Mobi(код извлечения: r4i3) версия для лучшего чтения.