JavaScript ( / ˈ dʒ ɑː v ə s k r ɪ p t / ), часто сокращенно JS , является языком программирования и базовой технологией Интернета , наряду с HTML и CSS . 99% веб-сайтов используют JavaScript на стороне клиента для поведения веб-страницы . [10]
Веб-браузеры имеют выделенный движок JavaScript , который выполняет клиентский код . Эти движки также используются на некоторых серверах и в различных приложениях . Самая популярная система выполнения для использования вне браузера — Node.js.
JavaScript — это высокоуровневый , часто компилируемый «точно в срок» язык, соответствующий стандарту ECMAScript . [11] Он имеет динамическую типизацию , прототипную объектную ориентацию и первоклассные функции . Он является многопарадигмальным , поддерживает событийно-управляемый , функциональный и императивный стили программирования . Он имеет интерфейсы прикладного программирования (API) для работы с текстом, датами, регулярными выражениями , стандартными структурами данных и объектной моделью документа (DOM).
Стандарт ECMAScript не включает в себя никаких входов/выходов (I/O), таких как сетевые , хранилища или графические возможности. На практике веб-браузер или другая система выполнения предоставляет API JavaScript для ввода/вывода.
Хотя Java и JavaScript похожи по названию, синтаксису и соответствующим стандартным библиотекам , эти два языка различны и существенно различаются по своей структуре.
Первый популярный веб-браузер с графическим пользовательским интерфейсом , Mosaic , был выпущен в 1993 году. Доступный для нетехнических людей, он сыграл важную роль в быстром росте ранней Всемирной паутины . [12] Затем ведущие разработчики Mosaic основали корпорацию Netscape , которая выпустила более совершенный браузер, Netscape Navigator , в 1994 году. Он быстро стал наиболее используемым. [13]
В эти годы становления Интернета веб-страницы могли быть только статичными, не имея возможности динамического поведения после загрузки страницы в браузере. На процветающей сцене веб-разработки было желание устранить это ограничение, поэтому в 1995 году Netscape решила добавить язык программирования в Navigator. Они преследовали два пути достижения этого: сотрудничество с Sun Microsystems для внедрения языка Java , а также найм Брендана Эйха для внедрения языка Scheme . [6]
Целью был «язык для масс», [14] «чтобы помочь непрограммистам создавать динамичные, интерактивные веб-сайты ». [15] Руководство Netscape вскоре решило, что лучшим вариантом для Эйха будет разработка нового языка с синтаксисом, похожим на Java и менее похожим на Scheme или другие существующие языки сценариев . [5] [6] Хотя новый язык и его реализация интерпретатора назывались LiveScript, когда впервые были отправлены как часть бета-версии Navigator в сентябре 1995 года, название было изменено на JavaScript для официального релиза в декабре. [6] [1] [16] [17]
Выбор имени JavaScript вызвал путаницу, подразумевая, что оно напрямую связано с Java. В то время начался бум доткомов , и Java был популярным новым языком, поэтому Эйх посчитал имя JavaScript маркетинговым ходом Netscape. [14]
Microsoft дебютировала с Internet Explorer в 1995 году, что привело к войне браузеров с Netscape. На фронте JavaScript Microsoft создала свой собственный интерпретатор под названием JScript . [18]
Microsoft впервые выпустила JScript в 1996 году, вместе с первоначальной поддержкой CSS и расширений HTML . Каждая из этих реализаций заметно отличалась от своих аналогов в Netscape Navigator . [19] [20] Эти различия затрудняли разработчикам работу их веб-сайтов в обоих браузерах, что привело к широкому использованию логотипов «лучше всего просматривать в Netscape» и «лучше всего просматривать в Internet Explorer» в течение нескольких лет. [19] [21]
Брендан Эйх позже сказал об этом периоде: «Это все еще своего рода язык- помощник . Он считается медленным или раздражающим. Люди делают всплывающие окна или прокручивают сообщения в старой строке состояния внизу вашего старого браузера ». [14]
В ноябре 1996 года Netscape представила JavaScript в Ecma International в качестве отправной точки для стандартной спецификации, которой могли бы соответствовать все поставщики браузеров. Это привело к официальному выпуску первой спецификации языка ECMAScript в июне 1997 года.
Процесс стандартизации продолжался несколько лет, с выпуском ECMAScript 2 в июне 1998 года и ECMAScript 3 в декабре 1999 года. Работа над ECMAScript 4 началась в 2000 году. [18]
Однако усилия по полной стандартизации языка были подорваны тем, что Microsoft заняла все более доминирующее положение на рынке браузеров. К началу 2000-х годов доля рынка Internet Explorer достигла 95%. [22] Это означало, что JScript стал фактическим стандартом для клиентских скриптов в Интернете.
Microsoft изначально участвовала в процессе стандартизации и реализовала некоторые предложения в своем языке JScript, но в конечном итоге прекратила сотрудничество в работе над ECMA. Таким образом, ECMAScript 4 был законсервирован.
В период доминирования Internet Explorer в начале 2000-х годов клиентские скрипты находились в состоянии застоя. Это начало меняться в 2004 году, когда преемник Netscape, Mozilla , выпустил браузер Firefox . Firefox был хорошо принят многими, отобрав значительную долю рынка у Internet Explorer. [23]
В 2005 году Mozilla присоединилась к ECMA International, и началась работа над стандартом ECMAScript для XML (E4X). Это привело к тому, что Mozilla начала работать совместно с Macromedia (позже приобретенным Adobe Systems ), которые реализовывали E4X в своем языке ActionScript 3, который был основан на проекте ECMAScript 4. Целью стала стандартизация ActionScript 3 как нового ECMAScript 4. С этой целью Adobe Systems выпустила реализацию Tamarin как проект с открытым исходным кодом . Однако Tamarin и ActionScript 3 слишком отличались от устоявшихся клиентских сценариев, и без сотрудничества с Microsoft ECMAScript 4 так и не был реализован.
Между тем, очень важные события происходили в сообществах с открытым исходным кодом, не связанных с работой ECMA. В 2005 году Джесси Джеймс Гарретт выпустил официальный документ, в котором он ввел термин Ajax и описал набор технологий, основой которых был JavaScript, для создания веб-приложений , в которых данные могут загружаться в фоновом режиме, избегая необходимости полной перезагрузки страницы. Это вызвало период возрождения JavaScript, возглавляемый библиотеками с открытым исходным кодом и сообществами, которые сформировались вокруг них. Было создано много новых библиотек, включая jQuery , Prototype , Dojo Toolkit и MooTools .
Google дебютировал со своим браузером Chrome в 2008 году с движком JavaScript V8 , который был быстрее, чем у конкурентов. [24] [25] Ключевым нововведением была компиляция «на лету» (JIT), [26] поэтому другим поставщикам браузеров пришлось переделать свои движки для JIT. [27]
В июле 2008 года эти разрозненные стороны собрались на конференции в Осло . Это привело к окончательному соглашению в начале 2009 года объединить всю соответствующую работу и продвигать язык вперед. Результатом стал стандарт ECMAScript 5, выпущенный в декабре 2009 года.
Амбициозная работа над языком продолжалась несколько лет, завершившись обширным набором дополнений и улучшений, которые были формализованы с публикацией ECMAScript 6 в 2015 году. [28]
Создание Node.js в 2009 году Райаном Далем вызвало значительный рост использования JavaScript за пределами веб-браузеров. Node объединяет движок V8 , цикл событий и API ввода-вывода , тем самым предоставляя автономную систему выполнения JavaScript. [29] [30] По состоянию на 2018 год Node использовали миллионы разработчиков, [31] а npm имел наибольшее количество модулей среди всех менеджеров пакетов в мире. [32]
В настоящее время проект спецификации ECMAScript открыто поддерживается на GitHub , [33] а редакции выпускаются посредством регулярных ежегодных снимков. [33] Потенциальные изменения языка проверяются посредством комплексного процесса предложений. [34] [35] Теперь вместо номеров редакций разработчики проверяют статус будущих функций по отдельности. [33]
Текущая экосистема JavaScript имеет множество библиотек и фреймворков , устоявшиеся практики программирования и значительное использование JavaScript за пределами веб-браузеров. [17] Кроме того, с ростом одностраничных приложений и других веб-сайтов, использующих JavaScript, было создано несколько транспайлеров для облегчения процесса разработки. [36]
«JavaScript» является торговой маркой Oracle Corporation в США. [37] [38] Торговая марка была первоначально выдана Sun Microsystems 6 мая 1997 года и была передана Oracle, когда они приобрели Sun в 2009 году. [39]
JavaScript является доминирующим клиентским языком сценариев в Интернете, и 99% всех веб-сайтов используют его для этой цели. [10] Скрипты встраиваются или включаются в HTML- документы и взаимодействуют с DOM .
Все основные веб-браузеры имеют встроенный движок JavaScript , который выполняет код на устройстве пользователя.
Более 80% веб-сайтов используют сторонние библиотеки JavaScript или веб-фреймворки как часть своих клиентских скриптов. [40]
jQuery на сегодняшний день является наиболее используемым. [40] Другие известные из них включают Angular , Bootstrap , Lodash , Modernizr , React , Underscore и Vue . [40] Несколько вариантов могут использоваться совместно, например jQuery и Bootstrap. [41]
Однако термин «Vanilla JS» был придуман для веб-сайтов, которые вообще не используют никаких библиотек или фреймворков, а вместо этого полностью полагаются на стандартную функциональность JavaScript. [42]
Использование JavaScript вышло за рамки его веб-браузерных корней. Движки JavaScript теперь встроены в ряд других программных систем, как для серверных веб-сайтов, так и для небраузерных приложений .
Первоначальные попытки продвижения использования JavaScript на стороне сервера были предприняты Netscape Enterprise Server и Microsoft Internet Information Services , [ 43] [44], но они были небольшими нишами. [45] Использование на стороне сервера в конечном итоге начало расти в конце 2000-х годов с созданием Node.js и других подходов . [45]
Electron , Cordova , React Native и другие фреймворки приложений использовались для создания множества приложений с поведением, реализованным в JavaScript. Другие небраузерные приложения включают поддержку Adobe Acrobat для создания сценариев PDF- документов [46] и расширения GNOME Shell, написанные на JavaScript. [47]
JavaScript использовался в некоторых встроенных системах , обычно с использованием Node.js. [48] [49] [50]
Движок JavaScript — это программный компонент , который выполняет код JavaScript . Первые движки JavaScript были просто интерпретаторами , но все соответствующие современные движки используют компиляцию just-in-time для повышения производительности. [51]
JavaScript-движки обычно разрабатываются поставщиками веб-браузеров , и у каждого крупного браузера есть один. В браузере JavaScript-движок работает совместно с движком рендеринга через Document Object Model и привязки Web IDL . [52] Однако использование JavaScript-движков не ограничивается браузерами; например, V8-движок является основным компонентом системы выполнения Node.js. [53 ]
Поскольку ECMAScript является стандартизированной спецификацией JavaScript, движок ECMAScript — это еще одно название для этих реализаций . С появлением WebAssembly некоторые движки также могут выполнять этот код в той же песочнице , что и обычный код JavaScript. [54] [53]JavaScript-движок должен быть встроен в систему выполнения (например, веб-браузер или автономную систему), чтобы скрипты могли взаимодействовать с более широкой средой. Система выполнения включает необходимые API для операций ввода/вывода , таких как сетевые операции , хранение и графика , а также обеспечивает возможность импорта скриптов.
JavaScript — однопоточный язык . Среда выполнения обрабатывает сообщения из очереди по одному за раз и вызывает функцию, связанную с каждым новым сообщением, создавая кадр стека вызовов с аргументами функции и локальными переменными . Стек вызовов сжимается и увеличивается в зависимости от потребностей функции. Когда стек вызовов пуст после завершения функции, JavaScript переходит к следующему сообщению в очереди. Это называется циклом событий , описываемым как «выполнение до завершения», поскольку каждое сообщение полностью обрабатывается до того, как будет рассмотрено следующее сообщение. Однако модель параллелизма языка описывает цикл событий как неблокирующий : программный ввод-вывод выполняется с использованием событий и функций обратного вызова . Это означает, например, что JavaScript может обрабатывать щелчок мыши, ожидая, пока запрос к базе данных вернет информацию. [55]
Наиболее известными автономными средами выполнения являются Node.js , Deno и Bun .
Следующие функции являются общими для всех соответствующих реализаций ECMAScript, если явно не указано иное.
JavaScript поддерживает большую часть структурированного синтаксиса программирования из C (например, if
операторы, while
циклы, switch
операторы, do while
циклы и т. д.). Одним частичным исключением является область действия : изначально в JavaScript была только область действия функций с var
; область действия блоков была добавлена в ECMAScript 2015 с ключевыми словами let
и const
. Как и в C, JavaScript делает различие между выражениями и операторами . Одним синтаксическим отличием от C является автоматическая вставка точки с запятой , которая позволяет опускать точки с запятой (которые завершают операторы). [56]
JavaScript слабо типизирован , что означает, что определенные типы неявно приводятся в зависимости от используемой операции. [57]
+
оператор преобразует оба операнда в строку, если только оба операнда не являются числами. Это происходит потому, что оператор сложения дублируется как оператор конкатенации-
оператор всегда преобразует оба операнда в число.+
, -
) всегда приводят операнд к числуЗначения преобразуются в строки следующим образом: [57]
,
).[object Object]
где Object
— имя конструктора объекта.Значения преобразуются в числа путем преобразования в строки, а затем преобразования строк в числа. Эти процессы можно модифицировать, определив функции toString
и valueOf
в прототипе для преобразования строк и чисел соответственно.
JavaScript подвергся критике за способ реализации этих преобразований, поскольку сложность правил можно ошибочно принять за непоследовательность. [58] [57] Например, при добавлении числа к строке число будет преобразовано в строку перед выполнением конкатенации, но при вычитании числа из строки строка преобразуется в число перед выполнением вычитания.
Часто также упоминается, что {} + []
результатом является 0
(число). Это вводит в заблуждение: {}
интерпретируется как пустой блок кода вместо пустого объекта, а пустой массив преобразуется в число оставшимся унарным +
оператором. Если выражение заключено в скобки - ({} + [])
– фигурные скобки интерпретируются как пустой объект, и результат выражения такой, "[object Object]"
как и ожидалось. [57]
JavaScript динамически типизирован, как и большинство других языков сценариев . Тип связан со значением, а не с выражением. Например, переменная , изначально связанная с числом, может быть переназначена в строку . [59] JavaScript поддерживает различные способы проверки типа объектов, включая утиную типизацию . [60]
JavaScript включает eval
функцию, которая может выполнять операторы, представленные в виде строк, во время выполнения.
Прототипное наследование в JavaScript описано Дугласом Крокфордом следующим образом:
Вы создаете прототипы объектов, а затем... создаете новые экземпляры. Объекты изменяемы в JavaScript, поэтому мы можем дополнять новые экземпляры, давая им новые поля и методы. Затем они могут выступать в качестве прототипов для еще более новых объектов. Нам не нужны классы, чтобы создавать множество похожих объектов... Объекты наследуются от объектов. Что может быть более объектно-ориентированным, чем это? [61]
В JavaScript объект — это ассоциативный массив , дополненный прототипом (см. ниже); каждый ключ предоставляет имя для свойства объекта , и существует два синтаксических способа указать такое имя: точечная нотация ( obj.x = 10
) и скобочная нотация ( obj['x'] = 10
). Свойство может быть добавлено, перепривязано или удалено во время выполнения. Большинство свойств объекта (и любое свойство, принадлежащее цепочке наследования прототипа объекта) можно перечислить с помощью for...in
цикла.
JavaScript использует прототипы там, где многие другие объектно-ориентированные языки используют классы для наследования . [62] В JavaScript можно имитировать многие функции, основанные на классах, с помощью прототипов. [63]
Функции также являются конструкторами объектов, наряду со своей типичной ролью. Префикс вызова функции new создаст экземпляр прототипа, наследуя свойства и методы от конструктора (включая свойства от Object
прототипа). [64] ECMAScript 5 предлагает Object.create
метод, позволяющий явно создавать экземпляр без автоматического наследования от Object
прототипа (старые среды могут назначать прототип null
). [65] Свойство конструктора prototype
определяет объект, используемый для внутреннего прототипа нового объекта. Новые методы можно добавлять, изменяя прототип функции, используемой в качестве конструктора. Встроенные конструкторы JavaScript, такие как Array
или Object
, также имеют прототипы, которые можно изменять. Хотя можно изменять прототип Object
, это обычно считается плохой практикой, поскольку большинство объектов в JavaScript наследуют методы и свойства от Object
прототипа, и они могут не ожидать, что прототип будет изменен. [66]
В отличие от многих объектно-ориентированных языков, в JavaScript нет различия между определением функции и определением метода . Вместо этого различие происходит во время вызова функции. Когда функция вызывается как метод объекта, локальное ключевое слово this функции привязывается к этому объекту для этого вызова.
Функции JavaScript являются первоклассными ; функция считается объектом. [67] Таким образом, функция может иметь свойства и методы, такие как .call()
и .bind()
. [68]
Вложенная функция — это функция, определенная внутри другой функции. Она создается каждый раз при вызове внешней функции.
Кроме того, каждая вложенная функция образует лексическое замыкание : лексическая область действия внешней функции (включая любую константу, локальную переменную или значение аргумента) становится частью внутреннего состояния каждого объекта внутренней функции, даже после завершения выполнения внешней функции. [69]
JavaScript также поддерживает анонимные функции .
JavaScript поддерживает неявное и явное делегирование .
JavaScript изначально поддерживает различные реализации шаблонов ролей [70] на основе функций , такие как Traits [71] [72] и Mixins . [73] Такая функция определяет дополнительное поведение по крайней мере одним методом, привязанным к this
ключевому слову в ее function
теле. Затем роль должна быть делегирована явно через call
или apply
объектам, которым необходимо предоставить дополнительное поведение, которое не разделяется через цепочку прототипов.
В то время как явное делегирование на основе функций охватывает композицию в JavaScript, неявное делегирование уже происходит каждый раз, когда цепочка прототипов проходится, чтобы, например, найти метод, который может быть связан с объектом, но не принадлежит ему напрямую. Как только метод найден, он вызывается в контексте этого объекта. Таким образом, наследование в JavaScript охватывается автоматизмом делегирования, который привязан к свойству прототипа функций-конструкторов.
JavaScript — язык с нулевым индексом .
В функцию можно передать неограниченное количество параметров. Функция может получить к ним доступ через формальные параметры , а также через локальный arguments
объект. С помощью метода можно также создавать функции с переменным числом аргументовbind
.
Как и во многих скриптовых языках, массивы и объекты ( ассоциативные массивы в других языках) могут быть созданы с помощью краткого сокращенного синтаксиса. Фактически, эти литералы формируют основу формата данных JSON .
Подобно Perl , JavaScript также поддерживает регулярные выражения , которые обеспечивают краткий и мощный синтаксис для обработки текста, который является более сложным, чем встроенные строковые функции. [74]
JavaScript поддерживает обещания и Async/await для обработки асинхронных операций. [ необходима цитата ]
Встроенный объект Promise предоставляет функциональность для обработки обещаний и связывания обработчиков с конечным результатом асинхронного действия. Недавно в спецификации JavaScript были представлены методы комбинаторов, которые позволяют разработчикам объединять несколько обещаний JavaScript и выполнять операции на основе различных сценариев. Представлены следующие методы: Promise.race, Promise.all, Promise.allSettled и Promise.any.
Async/await позволяет структурировать асинхронную, неблокирующую функцию способом, аналогичным обычной синхронной функции. Асинхронный, неблокирующий код может быть написан с минимальными накладными расходами, структурированным аналогично традиционному синхронному, блокирующему коду.
Исторически некоторые движки JavaScript поддерживали следующие нестандартные функции:
catch
предложения (как в Java)function(args) expr
; этот экспериментальный синтаксис предшествовал стрелочным функциям)Переменные в JavaScript могут быть определены с использованием ключевых слов var
, [76] let
[77] или const
[78] . Переменные, определенные без ключевых слов, будут определены в глобальной области видимости.
// Объявляет переменную функциональной области действия с именем `x` и неявно присваивает ей // специальное значение `undefined`. Переменные без значения автоматически // устанавливаются в undefined. // var обычно считается плохой практикой, и let и const обычно предпочтительнее. var x ; // Переменные можно вручную установить в `undefined`, например, так: let x2 = undefined ; // Объявляет переменную с областью действия блока с именем `y` и неявно устанавливает ее в // `undefined`. Ключевое слово `let` было введено в ECMAScript 2015. let y ; // Объявляет блочную, непереназначаемую переменную с именем `z` и устанавливает ее в // строковый литерал. Ключевое слово `const` также было введено в ECMAScript 2015, // и должно быть явно назначено.// Ключевое слово `const` означает константу, поэтому переменную нельзя переназначить , // так как ее значение `constant`. const z = "это значение нельзя переназначить!" ; // Объявляет глобальную переменную и присваивает ей значение 3. Обычно это считается // плохой практикой и не будет работать, если включен строгий режим. t = 3 ; // Объявляет переменную с именем `myNumber` и присваивает ей числовой литерал (значение // `2`). let myNumber = 2 ; // Переназначает `myNumber`, устанавливая его в строковый литерал (значение `"foo"`). // JavaScript — язык с динамической типизацией, поэтому это допустимо. myNumber = "foo" ;
Обратите внимание на комментарии в приведенных выше примерах, все из которых предваряются двумя косыми чертами .
В JavaScript нет встроенной функциональности ввода/вывода , вместо этого она предоставляется средой выполнения. Спецификация ECMAScript в редакции 5.1 упоминает, что «в этой спецификации нет положений для ввода внешних данных или вывода вычисленных результатов». [79]
Однако большинство сред выполнения имеют console
объект, который можно использовать для печати вывода. [80] Вот минималистская программа «Hello, World!» на JavaScript в среде выполнения с объектом console:
console.log ( "Привет, мир! " ) ;
В HTML-документах для вывода требуется такая программа:
// Текстовые узлы можно создавать с помощью метода "write". // Это не приветствуется, так как может привести к перезаписи документа, если он полностью загружен. document . write ( 'foo' );// Элементы тоже можно создавать. Сначала их нужно создать в DOM. const myElem = document . createElement ( 'span' ); // Также можно задать такие атрибуты, как классы и идентификатор myElem . classList . add ( 'foo' ); myElem . id = 'bar' ; // После установки этого тег будет выглядеть так: `<span class="foo" id="bar" data-attr="baz"></span>` myElem . setAttribute ( 'data-attr' , 'baz' ); // Что также можно записать как `myElem.dataset.attr = 'baz'` // Наконец, добавляем его как дочерний элемент к <body> в HTML- документе . body . appendChild ( myElem );// Элементы могут быть императивно захвачены с помощью querySelector для одного элемента или querySelectorAll для нескольких элементов, которые могут быть зациклены с помощью forEach document . querySelector ( '.class' ); // Выбирает первый элемент с классом "class" document . querySelector ( '#id' ); // Выбирает первый элемент с `id` из "id" document . querySelector ( '[data-other]' ); // Выбирает первый элемент с атрибутом "data-other" document . querySelectorAll ( '.multiple' ); // Возвращает похожий на массив NodeList всех элементов с классом "multiple"
Простая рекурсивная функция для вычисления факториала натурального числа :
function factorial ( n ) { // Проверка аргумента на легитимность. Факториал определен для положительных целых чисел. if ( isNaN ( n )) { console . error ( "Нечисловой аргумент не допускается." ); return NaN ; // Специальное значение: не число } if ( n === 0 ) return 1 ; // 0! = 1 if ( n < 0 ) return undefined ; // Факториал отрицательных чисел не определен. if ( n % 1 ) { console . warn ( ` ${ n } будет округлен до ближайшего целого числа. Для нецелых чисел рассмотрите возможность использования гамма-функции.` ); n = Math . round ( n ); } // Вышеуказанные проверки не нужно повторять в рекурсии, поэтому фактическая рекурсивная часть определяется отдельно ниже. // Следующая строка — это выражение функции для рекурсивного вычисления факториала. Оно использует синтаксис стрелок, введенный в ES6. const recursivelyCompute = a => a > 1 ? a * recursivelyCompute ( a - 1 ) : 1 ; // Обратите внимание на использование тернарного оператора `?`. return recursivelyCompute ( n ); } факториал ( 3 ); // Возвращает 6
Анонимная функция ( или лямбда):
константный счетчик = функция () { пусть счетчик = 0 ; возврат функции () { возврат ++ счетчик ; } }; const x = counter (); x (); // Возвращает 1 x (); // Возвращает 2 x (); // Возвращает 3
Этот пример показывает, что в JavaScript замыкания функций захватывают свои нелокальные переменные по ссылке.
Стрелочные функции впервые были введены в 6-м издании – ECMAScript 2015. Они сокращают синтаксис для написания функций в JavaScript. Стрелочные функции анонимны, поэтому для ссылки на них, чтобы вызвать их после создания, необходима переменная, если только они не заключены в скобки и не выполняются немедленно.
Пример стрелочной функции:
// Стрелочные функции позволяют нам опустить ключевое слово `function`. // Здесь `long_example` указывает на анонимное значение функции. const long_example = ( input1 , input2 ) => { console . log ( "Hello, World!" ); const output = input1 + input2 ; возврат вывода ; }; // Если скобок нет, стрелочная функция просто возвращает выражение // Вот оно (input1 + input2) const short_example = ( input1 , input2 ) => input1 + input2 ; long_example ( 2 , 3 ); // Печатает "Hello, World!" и возвращает 5 short_example ( 2 , 5 ); // Возвращает 7 // Если стрелочная функция имеет только один параметр, скобки можно убрать. const no_parentheses = input => input + 2 ; без_скоб ( 3 ); // Возвращает 5 // Стрелочная функция, как и определения других функций, может быть выполнена в том же операторе, в котором она была создана. // Это полезно при написании библиотек, чтобы избежать заполнения глобальной области видимости, а также для замыканий. let three = (( a , b ) => a + b ) ( 1 , 2 ); const generate_multiplier_function = a => ( b => isNaN ( b ) || ! b ? a : a *= b ); const five_multiples = generate_multiplier_function ( 5 ); // Предоставленный аргумент «задает» выражение и сохраняется в a. five_multiples ( 1 ); // Возвращает 5 five_multiples ( 3 ); // Возвращает 15 five_multiples ( 4 ); // Возвращает 60
В JavaScript объекты могут быть созданы как экземпляры класса .
Пример класса объекта:
класс Мяч { конструктор ( радиус ) { этот . радиус = радиус ; этот . площадь = Математика . ПИ * ( радиус ** 2 ); } // Классы (и, следовательно , объекты) могут содержать функции, известные как методы show ( ) { console.log ( this.radius ) ; } }; const myBall = new Ball ( 5 ); // Создает новый экземпляр объекта-мяча с радиусом 5 myBall . radius ++ ; // Свойства объекта обычно можно изменять извне myBall . show (); // Использование унаследованной функции "show" регистрирует "6"
В JavaScript объекты можно создавать непосредственно из функции.
Пример функционирования объекта:
функция Шар ( радиус ) { const площадь = Математика . ПИ * ( радиус ** 2 ); const obj = { радиус , площадь }; // Объекты изменяемы, и функции могут быть добавлены как свойства. obj . show = () => console . log ( obj . radius ); return obj ; }; const myBall = Ball ( 5 ); // Создает новый объект мяча с радиусом 5. Ключевое слово "new" не требуется. myBall . radius ++ ; // Свойство экземпляра можно изменить. myBall . show (); // Использование функции "show" регистрирует "6" - новое значение экземпляра.
Демонстрация вариативной функцииarguments
( является специальной переменной ): [81]
функция сумма () { пусть x = 0 ; для ( пусть i = 0 ; i < аргументы . длина ; ++ i ) x += аргументы [ i ]; вернуть х ; } сумма ( 1 , 2 ); // Возвращает 3 сумма ( 1 , 2 , 3 ); // Возвращает 6 // Начиная с ES6, используется оператор rest. function sum (... args ) { return args . reduce (( a , b ) => a + b ); } сумма ( 1 , 2 ); // Возвращает 3 сумма ( 1 , 2 , 3 ); // Возвращает 6
Немедленно вызываемые выражения функций часто используются для создания замыканий. Замыкания позволяют собирать свойства и методы в пространстве имен и делать некоторые из них закрытыми:
пусть счетчик = ( функция () { пусть i = 0 ; // Частная собственность return { // Публичные методы get : function () { alert ( i ); }, set : function ( value ) { i = value ; }, increment : function () { alert ( ++ i ); } }; })(); // Модуль counter.get ( ); // Возвращает 0 counter.set ( 6 ) ; counter.increment ( ) ; // Возвращает 7 counter.increment ( ); // Возвращает 8
Объекты -генераторы (в форме функций-генераторов) предоставляют функцию, которую можно вызывать, выходить из нее и повторно входить в нее, сохраняя при этом внутренний контекст (сохранение состояния). [82]
функция * rawCounter () { выход 1 ; выход 2 ; } function * dynamicCounter () { let count = 0 ; while ( true ) { // В большинстве случаев не рекомендуется использовать циклы while true. yield ++ count ; } } // Экземпляры const counter1 = rawCounter (); const counter2 = dynamicCounter (); // Реализация counter1.next (); // {значение: 1 , выполнено: false} counter1.next ( ); // { значение : 2, выполнено: false} counter1.next ( ); // {значение: undefined , выполнено: true} counter2.next (); // {значение: 1, выполнено: false} counter2.next ( ); // {значение : 2, выполнено : false} counter2.next (); // {значение: 3, выполнено : false} // ... бесконечно
JavaScript может экспортировать и импортировать из модулей: [83]
Пример экспорта:
/* mymodule.js */ // Эта функция остается закрытой, так как она не экспортируется let sum = ( a , b ) => { return a + b ; } // Экспорт переменных export let name = 'Alice' ; export let age = 23 ; // Экспорт именованных функций export function add ( num1 , num2 ) { return num1 + num2 ; } // Экспорт класса экспорт класса Умножение { конструктор ( num1 , num2 ) { this . num1 = num1 ; this . num2 = num2 ; } добавить () { вернуть сумму ( this . num1 , this . num2 ); } }
Пример импорта:
// Импорт одного свойства import { add } from './mymodule.js' ; console . log ( add ( 1 , 2 )); //> 3 // Импорт нескольких свойств import { name , age } from './mymodule.js' ; console . log ( name , age ); //> "Alice", 23 // Импорт всех свойств из модуля import * from './module.js' console.log ( name , age ); // > "Alice", 23 console.log ( add ( 1 , 2 ) ); //> 3
В этом примере кода показаны различные функции JavaScript.
/* Находит наименьшее общее кратное (НОК) двух чисел */ function LCMCalculator ( x , y ) { // функция-конструктор if ( isNaN ( x * y )) throw new TypeError ( "Нечисловые аргументы не допускаются." ); const checkInt = function ( x ) { // внутренняя функция if ( x % 1 !== 0 ) throw new TypeError ( x + "не является целым числом" ); вернуть х ; }; this.a = checkInt ( x ) // точки с запятой ^^^^ необязательны, достаточно новой строки this.b = checkInt ( y ) ; } // Прототипом экземпляров объектов, созданных конструктором, является // свойство "prototype" этого конструктора. LCMCalculator.prototype = { // конструктор литерала объекта : LCMCalculator , // при переназначении прототипа установите свойство конструктора соответствующим образом gcd : function ( ) { // метод , вычисляющий наибольший общий делитель // Алгоритм Евклида : let a = Math.abs ( this.a ) , b = Math.abs ( this.b ) , t ; if ( a < b ) { // поменять местами переменные // t = b; b = a; a = t; [ a , b ] = [ b , a ]; // поменять местами с помощью деструктурирующего присваивания (ES6) } пока ( б !== 0 ) { т = б ; б = а % б ; а = т ; } // Нужно вычислить НОД только один раз, поэтому «переопределим» этот метод. // (На самом деле это не переопределение — оно определено в самом экземпляре, // так что this.gcd ссылается на это «переопределение» вместо LCMCalculator.prototype.gcd. // Обратите внимание, что это приводит к неправильному результату, если члены объекта LCMCalculator «a» или «b» впоследствии изменяются.) // Кроме того, 'gcd' === «gcd», this['gcd'] === this.gcd this [ 'gcd' ] = function () { return a ; }; вернуть ; } , // Имена свойств объектов могут быть указаны строками, разделенными двойными (") или одинарными (') кавычками. "lcm" : function () { // Имена переменных не конфликтуют со свойствами объектов, например, |lcm| не является |this.lcm|. // не используем |this.a*this.b|, чтобы избежать проблем с точностью FP let lcm = this . a / this . gcd () * this . b ; // Нужно вычислить lcm только один раз, поэтому «переопределим» этот метод. this . lcm = function () { return lcm ; }; вернуть lcm ; }, // Методы также можно объявлять с использованием синтаксиса ES6 toString () { // Использование как шаблонных литералов ES6, так и оператора (+) для объединения значений return `LCMCalculator: a = ${ this . a } , b = ` + this . b ; } }; // Определяем универсальную функцию вывода ; эта реализация работает только для веб - браузеров function output ( x ) { document.body.appendChild ( document.createTextNode ( x ) ) ; document.body.appendChild ( document.createElement ( ' br ' ) ) ; } // Примечание: map() и forEach() массива определены в JavaScript 1.6. // Они используются здесь для демонстрации функциональной природы JavaScript. [ [ 25 , 55 ], [ 21 , 56 ], [ 22 , 58 ], [ 28 , 56 ] ]. map ( function ( pair ) { // литерал массива + функция сопоставления return new LCMCalculator ( pair [ 0 ], pair [ 1 ]); }). sort (( a , b ) => a . lcm () - b . lcm ()) // сортировка с помощью этой сравнительной функции; => — сокращенная форма функции, называемая «стрелочной функцией» . forEach ( printResult ); функция printResult ( obj ) { output ( obj + ", gcd = " + obj . gcd () + ", lcm = " + obj . lcm ()); }
В окне браузера должен отобразиться следующий вывод.
НОККалькулятор: a = 28, b = 56, НОД = 28, НОК = 56 НОККалькулятор: a = 21, b = 56, НОД = 7, НОК = 168 НОККалькулятор: a = 25, b = 55, НОД = 5, НОК = 275 НОККалькулятор: a = 22, b = 58, НОД = 2, НОК = 638
JavaScript и DOM предоставляют возможность авторам-злоумышленникам доставлять скрипты для запуска на клиентском компьютере через Интернет. Авторы браузеров минимизируют этот риск, используя два ограничения. Во-первых, скрипты запускаются в песочнице , в которой они могут выполнять только действия, связанные с Интернетом, а не задачи программирования общего назначения, такие как создание файлов. Во-вторых, скрипты ограничены политикой одного источника : скрипты с одного веб-сайта не имеют доступа к информации, такой как имена пользователей, пароли или файлы cookie, отправленные на другой сайт. Большинство ошибок безопасности, связанных с JavaScript, являются нарушениями либо политики одного источника, либо песочницы.
Существуют подмножества общего JavaScript — ADsafe, Secure ECMAScript (SES) — которые обеспечивают более высокий уровень безопасности, особенно в коде, созданном третьими лицами (например, рекламе). [84] [85] Closure Toolkit — еще один проект для безопасного внедрения и изоляции стороннего JavaScript и HTML. [86]
Политика безопасности контента — это основной метод обеспечения того, чтобы на веб-странице выполнялся только доверенный код.
Распространенной проблемой безопасности, связанной с JavaScript, является межсайтовый скриптинг (XSS), нарушение политики одного источника . Уязвимости XSS возникают, когда злоумышленник может заставить целевой веб-сайт, например веб-сайт онлайн-банкинга, включить вредоносный скрипт в веб-страницу, представленную жертве. Скрипт в этом примере затем может получить доступ к банковскому приложению с привилегиями жертвы, потенциально раскрывая секретную информацию или переводя деньги без разрешения жертвы. Одним из важных решений уязвимостей XSS является очистка HTML .
Некоторые браузеры включают частичную защиту от отраженных атак XSS, в которых злоумышленник предоставляет URL, включающий вредоносный скрипт. Однако даже пользователи этих браузеров уязвимы для других атак XSS, например, тех, где вредоносный код хранится в базе данных. Только правильная разработка веб-приложений на стороне сервера может полностью предотвратить XSS.
Уязвимости XSS также могут возникать из-за ошибок реализации авторами браузеров. [87]
Другая уязвимость кросс-сайта — это подделка кросс-сайтовых запросов (CSRF). При CSRF код на сайте злоумышленника обманывает браузер жертвы, заставляя его выполнять действия, которые пользователь не планировал на целевом сайте (например, перевод денег в банке). Когда целевые сайты полагаются исключительно на файлы cookie для аутентификации запросов, запросы, исходящие из кода на сайте злоумышленника, могут содержать те же действительные учетные данные для входа инициирующего пользователя. В общем, решение CSRF заключается в требовании значения аутентификации в скрытом поле формы, а не только в файлах cookie, для аутентификации любого запроса, который может иметь долгосрочные последствия. Проверка заголовка HTTP Referrer также может помочь.
«JavaScript hijacking» — это тип CSRF-атаки, при которой <script>
тег на сайте злоумышленника использует страницу на сайте жертвы, которая возвращает конфиденциальную информацию, такую как JSON или JavaScript. Возможные решения включают:
Разработчики клиент-серверных приложений должны осознавать, что ненадежные клиенты могут находиться под контролем злоумышленников. Автор приложения не может предполагать, что его код JavaScript будет работать так, как задумано (или вообще), поскольку любой секрет, встроенный в код, может быть извлечен определенным противником. Вот некоторые выводы:
Системы управления пакетами, такие как npm и Bower, популярны среди разработчиков JavaScript. Такие системы позволяют разработчику легко управлять зависимостями своей программы от программных библиотек других разработчиков. Разработчики верят, что сопровождающие библиотеки будут поддерживать их в безопасности и актуальности, но это не всегда так. Из-за этого слепого доверия возникла уязвимость. У используемых библиотек могут быть новые выпуски, которые приводят к появлению ошибок или уязвимостей во всех программах, которые полагаются на библиотеки. И наоборот, библиотека может оставаться неисправленной с известными уязвимостями в свободном доступе. В исследовании, проведенном с использованием выборки из 133 000 веб-сайтов, исследователи обнаружили, что 37% веб-сайтов включали библиотеку по крайней мере с одной известной уязвимостью. [90] «Средняя задержка между самой старой версией библиотеки, используемой на каждом веб-сайте, и самой новой доступной версией этой библиотеки составляет 1177 дней в ALEXA, а разработка некоторых библиотек, которые все еще активно используются, прекратилась много лет назад». [90] Другая возможность заключается в том, что разработчик библиотеки может полностью удалить библиотеку. Это произошло в марте 2016 года, когда Азер Кочулу удалил свой репозиторий из npm. Это привело к поломке десятков тысяч программ и веб-сайтов, зависящих от его библиотек. [91] [92]
JavaScript предоставляет интерфейс для широкого спектра возможностей браузера, некоторые из которых могут иметь недостатки, такие как переполнение буфера . Эти недостатки могут позволить злоумышленникам писать скрипты, которые будут запускать любой код, который они пожелают, в системе пользователя. Этот код никоим образом не ограничивается другим приложением JavaScript. Например, эксплойт переполнения буфера может позволить злоумышленнику получить доступ к API операционной системы с привилегиями суперпользователя.
Эти уязвимости затронули основные браузеры, включая Firefox, [93] Internet Explorer, [94] и Safari. [95]
Плагины, такие как видеоплееры, Adobe Flash и широкий спектр элементов управления ActiveX , включенных по умолчанию в Microsoft Internet Explorer, также могут иметь уязвимости, которые можно эксплуатировать через JavaScript (такие уязвимости эксплуатировались в прошлом). [96] [97]
В Windows Vista компания Microsoft попыталась ограничить риски ошибок, таких как переполнение буфера, запустив процесс Internet Explorer с ограниченными привилегиями. [98] Google Chrome аналогичным образом ограничивает свои средства визуализации страниц их собственной « песочницей ».
Веб-браузеры способны запускать JavaScript вне песочницы с привилегиями, необходимыми, например, для создания или удаления файлов. Такие привилегии не предназначены для предоставления коду из Интернета.
Неправильное предоставление привилегий JavaScript из Интернета сыграло свою роль в уязвимостях как в Internet Explorer [99], так и в Firefox. [100] В Windows XP Service Pack 2 компания Microsoft понизила привилегии JScript в Internet Explorer. [101]
Microsoft Windows позволяет запускать исходные файлы JavaScript на жестком диске компьютера как программы общего назначения, не изолированные в песочнице (см.: Windows Script Host ). Это делает JavaScript (как и VBScript ) теоретически жизнеспособным вектором для троянского коня , хотя троянские кони JavaScript на практике встречаются редко. [102] [ проверка не удалась ]
В 2015 году в статье исследователей безопасности была описана реализация концепции атаки Rowhammer на основе JavaScript. [103] [104] [105] [106]
В 2017 году была продемонстрирована атака на основе JavaScript через браузер, которая может обойти ASLR . Она называется «ASLR⊕Cache» или AnC. [107] [108]
В 2018 году в статье, в которой было объявлено об атаках Spectre против спекулятивного выполнения в процессорах Intel и других, была представлена реализация JavaScript. [109]
Важные инструменты развивались вместе с языком.
Распространенное заблуждение заключается в том, что JavaScript напрямую связан с Java . Оба языка действительно имеют синтаксис, подобный C (язык C является их самым непосредственным общим языком-предком). Они также обычно изолированы , и JavaScript был разработан с учетом синтаксиса и стандартной библиотеки Java. В частности, все ключевые слова Java были зарезервированы в оригинальном JavaScript, стандартная библиотека JavaScript следует соглашениям об именовании Java, а JavaScript Math
и Date
объекты основаны на классах из Java 1.0. [112]
Оба языка впервые появились в 1995 году, но Java был разработан Джеймсом Гослингом из Sun Microsystems, а JavaScript — Бренданом Эйхом из Netscape Communications.
Различия между этими двумя языками более заметны, чем их сходства. Java имеет статическую типизацию , в то время как типизация JavaScript является динамической . Java загружается из скомпилированного байт-кода, тогда как JavaScript загружается как исходный код, понятный человеку. Объекты Java основаны на классах , в то время как JavaScript основаны на прототипах . Наконец, Java не поддерживал функциональное программирование до Java 8, тогда как JavaScript поддерживал его с самого начала, находясь под влиянием Scheme .
JSON — это формат данных, полученный из JavaScript; отсюда и название JavaScript Object Notation. Это широко используемый формат, поддерживаемый многими другими языками программирования.
Многие веб-сайты используют много JavaScript, поэтому были созданы транспиляторы для преобразования кода, написанного на других языках, что может помочь в процессе разработки. [36]
TypeScript и CoffeeScript — два известных языка, которые транслируются в JavaScript.
WebAssembly — это новый язык с форматом байт-кода, разработанный для дополнения JavaScript, особенно критических для производительности частей скриптов веб-страниц . Все основные движки JavaScript поддерживают WebAssembly, [113] который работает в той же песочнице, что и обычный код JavaScript.
asm.js — это подмножество JavaScript, которое послужило предшественником WebAssembly. [114]
Айх: Первоочередной заботой Netscape было то, чтобы он выглядел как Java.
Eich: "function", восемь букв, на меня повлиял AWK.
{{cite web}}
: CS1 maint: unfit URL (link)[С] момента появления в 1996 г. версии JScript 1.0... мы наблюдаем устойчивый рост использования JScript на сервере, особенно в Active Server Pages (ASP)