Python против JavaScript для любителей питона

Оглавление

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

Если ты серьезно относишься к веб-разработка, затем вам нужно будет изучить JavaScript в какой-то момент. Из года в год многочисленные опросы показывают, что JavaScript является одним из самых популярных языков программирования в мире, с большим и растущим сообществом разработчиков. Как и Python, современный JavaScript можно использовать практически везде, включая интерфейс, серверную часть, настольные компьютеры, мобильные устройства и Интернет вещей (IoT). Иногда выбор между Python и JavaScript может быть неочевидным.

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

В этой статье вы узнаете, как:

  • Сравнение Python и JavaScript
  • Выберите подходящий язык для работы
  • Написать сценарий оболочки на JavaScript
  • Создание динамического контента на веб-странице
  • Воспользуйтесь преимуществами экосистемы JavaScript
  • Избегайте распространенных ошибок в JavaScript

Бесплатный бонус: 5 Размышления о мастерстве владения Python, бесплатный курс для разработчиков Python, который показывает вам план действий и мышление, с которым вы будете работать. вам нужно поднять свои навыки работы с Python на новый уровень.

Краткий обзор JavaScript

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

Это не Java!

Многие люди, особенно некоторые ИТ-специалисты по подбору персонала, считают, что JavaScript и Java - это один и тот же язык. Однако их трудно винить, потому что изобретение такого знакомо звучащего названия было маркетинговым ходом.

Изначально JavaScript назывался Mocha, затем был переименован в LiveScript и, наконец, переименован в JavaScript незадолго до его выхода. В то время Java была многообещающей веб-технологией, но она была слишком сложна для нетехнических веб-мастеров. JavaScript был задуман как несколько похожий, но удобный для начинающих язык, дополняющий Java-апплеты в веб-браузерах.

Интересный факт: И Java, и JavaScript были выпущены в 1995 году. Python было уже пять лет.

Чтобы еще больше запутать ситуацию, Microsoft разработала свою собственную версию языка, которую она назвала JScript из-за отсутствия лицензионных прав, для использования с Internet Explorer 3.0. Сегодня люди часто ссылаются на в JavaScript как JS.

Хотя Java и JavaScript имеют несколько общих черт в своем синтаксисе, подобном C, а также в своих стандартных библиотеках, они используются для разных целей. Java отошел от клиентской части и стал языком более общего назначения. JavaScript, несмотря на свою простоту, был достаточен для проверки правильности HTML-форм и добавления небольшой анимации.

Это ECMAScript

JavaScript был разработан на заре развития Интернета относительно небольшой компанией, известной как Netscape. Чтобы завоевать рынок у Microsoft и сгладить различия между веб-браузерами, Netscape необходимо было стандартизировать свой язык. Получив отказ от международного консорциума всемирной паутины (W3C), они обратились в европейский орган по стандартизации под названием ECMA (на сегодняшний день).Ecma International) за помощью.

ECMA определила формальную спецификацию для языка под названием ECMAScript, поскольку название JavaScript было зарегистрировано как торговая марка Sun Microsystems. JavaScript стал одной из реализаций спецификации, которую он изначально вдохновлял.

Примечание: Другими словами, JavaScript соответствует спецификации ECMAScript. Другим заметным представителем семейства ECMAScript является ActionScript, который используется на платформе Flash.

Хотя отдельные реализации спецификации в некоторой степени соответствовали ECMAScript, они также поставлялись с дополнительными проприетарными API. Это привело к неправильному отображению веб-страниц в разных браузерах и появлению таких библиотек, как jQuery.

Есть Ли Другие Скрипты?

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

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

  • Многофункциональные интернет-приложения: Flash, Silverlight, JavaFX
  • Переводчики: Haxe, Google Web Toolkit, pyjs
  • Диалекты JavaScript: CoffeeScript, TypeScript

Эти попытки были продиктованы не только личными предпочтениями, но и ограничениями веб-браузеров до появления HTML5. В те дни вы не могли использовать JavaScript для задач, требующих больших вычислительных затрат, таких как рисование векторной графики или обработка аудио.

С другой стороны, Rich Internet Applications (RIA) благодаря подключаемым модулям обеспечивали возможность погружения в браузер, как на рабочем столе. Они отлично подходили для игр и обработки мультимедийных данных. К сожалению, большинство из них были с закрытым исходным кодом. Некоторые из них имели уязвимости в системе безопасности или проблемы с производительностью на определенных платформах. В довершение всего, все они серьезно ограничивали возможности поисковых систем индексировать страницы, созданные с помощью этих плагинов.

Примерно в то же время появились транспиляторы, которые позволяли автоматически переводить другие языки на JavaScript. Это значительно снизило барьер для входа в front-end разработку, поскольку инженеры-разработчики могли использовать свои навыки в новой области. Однако недостатками были более медленное время разработки, ограниченная поддержка веб-стандартов и трудоемкая отладка транслируемого кода JavaScript. Чтобы привязать его обратно к исходному коду, вам понадобится исходная карта.

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

Чтобы написать код на Python для браузера, вы можете использовать один из доступных транспиляторов, таких как Transcrypt или pyjs. Последний является портом Google Web Toolkit (GWT), который был чрезвычайно популярным преобразователем Java в JavaScript. Другой вариант - использовать инструмент, подобный Brython, который запускает упрощенную версию интерпретатора Python на чистом JavaScript. Однако преимущества могут быть сведены на нет низкой производительностью и отсутствием совместимости.

Транспонирование позволило создать множество новых языков, призванных заменить JavaScript и устранить его недостатки. Некоторые из этих языков были тесно связаны с диалектами JavaScript. Возможно, первым был CoffeeScript, который был создан около десяти лет назад. Одним из последних был Google Dart, который стал самым быстрорастущим языком в 2019 году по данным GitHub. За ним последовало еще много языков, но большинство из них сейчас устарели из-за последних достижений в области JavaScript.

Одним из вопиющих исключений является Microsoft TypeScript, который в последние годы приобрел большую популярность. Это полностью совместимый надмножество JavaScript, который добавляет необязательную статическую проверку типов. Если это звучит для вас знакомо, то это потому, что подсказка типа в Python была вдохновлена TypeScript.

Search Interest in TypeScript According to Google Trends Ищите интерес к TypeScript в соответствии с Google Trends

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

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

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

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

В дополнение к этому стоит отметить, что современные браузеры поддерживают стандарт WebAssembly, который является довольно новой технологией. Он определяет двоичный формат кода, который может выполняться в браузере с почти естественной производительностью. Он быстрый, переносимый, безопасный и позволяет выполнять перекрестную компиляцию кода, написанного на таких языках, как C++ или Rust. С его помощью, например, вы могли бы взять устаревший код вашей любимой видеоигры и запустить его в браузере.

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

Примечание: Для более глубокого погружения в WebAssembly ознакомьтесь с Подкастом Real Python - Эпизод 154 с Бреттом Кэнноном.

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

Стартовый набор для JavaScript

Одно из первых сходств, которое вы заметите при сравнении Python и JavaScript, заключается в том, что входные барьеры для обоих языков довольно низкие, что делает оба языка очень привлекательными для начинающих, которые хотели бы научиться программировать. Для JavaScript единственным начальным требованием является наличие веб-браузера. Если вы читаете это, значит, вы уже в курсе. Такая доступность способствует популярности языка.

Адресная строка

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

JavaScript in the Address Bar

Буквальный текст равен javascript:alert('hello world'), но не просто копируйте и вставляйте его!

Эта часть после префикса javascript: является фрагментом кода JavaScript. После подтверждения в вашем браузере должно появиться диалоговое окно с сообщением hello world. Каждый браузер отображает это диалоговое окно немного по-разному. Например, в Google Chrome это отображается следующим образом:

Alert Dialog Box in JavaScript

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

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

Инструменты веб-разработчика

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

Примечание: В приведенных ниже примерах используется Браузер Google Chrome версии 80.0. Сочетания клавиш могут отличаться в других браузерах, но интерфейс должен быть в основном таким же.

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

  • F12

  • Ctrl+Сдвиг+I

  • Cmd+Опция+Я

Эта функция может быть отключена по умолчанию, например, если вы используете Apple Safari или Microsoft Edge. Как только инструменты веб-разработчика будут активированы, вы увидите множество вкладок и панелей инструментов с содержимым, похожим на это:

Web Developer Tools in Google Chrome Инструменты веб-разработчика в Google Chrome

В совокупности это мощная среда разработки, оснащенная отладчиком JavaScript, профилировщиком производительности и памяти, менеджером сетевого трафика и многим, многим другим. Существует даже удаленный отладчик для физических устройств, подключенных по USB-кабелю!

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

Консоль в основном используется для проверки сообщений журнала, отправляемых текущей веб-страницей, но она также может стать отличным пособием для изучения JavaScript. Как и в случае с интерактивным интерпретатором Python, вы можете ввести код JavaScript непосредственно в консоль, чтобы он выполнялся "на лету":

JavaScript Console in Web Developer Tools Консоль JavaScript в инструментах веб-разработчика

В нем есть все, что вы ожидаете от обычного инструмента REPL и даже больше. В частности, консоль поддерживает подсветку синтаксиса, контекстное автозаполнение, историю команд, редактирование строк, аналогичное GNU Readline, и возможность визуализации интерактивных элементов. Его возможности рендеринга могут быть особенно полезны для анализа объектов и табличных данных, перехода к исходному коду из трассировки стека или просмотра HTML-элементов.

Вы можете записывать пользовательские сообщения в консоль, используя предопределенный объект console. console.log() в JavaScript является эквивалентом в Python print():

console.log('hello world');

После этого сообщение появится на вкладке консоль в инструментах веб-разработчика. Кроме того, есть еще несколько полезных методов, доступных в console объекте.

HTML-документ

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

Method Code Example
HTML Element’s Attribute <button onclick="alert('hello');">Click</button>
HTML <script> Tag <script>alert('hello');</script>
External File <script src="/path/to/file.js"></script>

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

Чаще всего встречается один или несколько тегов <script>, ссылающихся на внешние файлы с кодом JavaScript. Эти файлы могут обслуживаться как локальным, так и удаленным веб-сервером.

Тег <script> может отображаться в любом месте документа, если он вложен либо в тег <head>, либо в тег <body>:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Home Page</title>
  <script src="https://server.com/library.js"></script>
  <script src="local/assets/app.js"></script>
  <script>
    function add(a, b) {
      return a + b;
    }
  </script>
</head>
<body>
  <p>Lorem ipsum dolor sit amet (...)</p>
  <script>
    console.log(add(2, 3));
  </script>
</body>
</html>

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

На всякий случай всегда указывайте теги <script> в нижней части текста документа:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Home Page</title>
</head>
<body>
  <p>Lorem ipsum dolor sit amet (...)</p>
  <script src="https://server.com/library.js"></script>
  <script src="local/assets/app.js"></script>
  <script>
    function add(a, b) {
      return a + b;
    }
  </script>
  <script>
    console.log(add(2, 3));
  </script>
</body>
</html>

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

<script src="https://server.com/library.js" defer></script>

Если вы хотите узнать больше о смешивании JavaScript с HTML, то ознакомьтесь с Учебным пособием по JavaScript от W3Schools.

Node.js

Вам больше не нужен веб-браузер для выполнения кода JavaScript. Существует инструмент под названием Node.js , который предоставляет среду выполнения для серверного JavaScript.

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

Web Browser JavaScript Engine
Apple Safari JavaScriptCore
Microsoft Edge V8
Microsoft IE Chakra
Mozilla Firefox SpiderMonkey
Google Chrome V8

Каждый из них реализован и поддерживается своим поставщиком. Однако для конечного пользователя нет заметной разницы, за исключением производительности отдельных движков. Node.js используется тот же движок V8, разработанный Google для браузера Chrome.

При запуске JavaScript в веб-браузере обычно требуется иметь возможность реагировать на щелчки мыши, динамически добавлять HTML-элементы или, возможно, получать изображение с веб-камеры. Но это не имеет смысла в условиях Node.js приложение, которое запускается вне браузера.

После того, как вы установили Node.js для своей платформы, вы можете выполнить код JavaScript точно так же, как в интерпретаторе Python. Чтобы начать интерактивный сеанс, перейдите в свой терминал и введите node:

$ node
> 2 + 2
4

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

> alert('hello world');
Thrown:
ReferenceError: alert is not defined

Это потому, что в вашей среде выполнения отсутствует другой компонент - API браузера. В то же время Node.js предоставляет набор API-интерфейсов, которые полезны во внутренних приложениях, таких как API файловой системы:

> const fs = require('fs');
> fs.existsSync('/path/to/file');
false

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

Если стандартная библиотека не удовлетворяет вашим потребностям, вы всегда можете установить сторонний пакет с помощью Node Package Manager (npm), который поставляется вместе со средой Node.js . Чтобы просмотреть или выполнить поиск пакетов, перейдите в npm общедоступный реестр, который похож на Индекс пакетов Python (PyPI).

Аналогично команде python, вы можете запускать скрипта с помощью Node.js:

$ echo "console.log('hello world');" > hello.js
$ node hello.js
hello world

Указывая путь к текстовому файлу с кодом JavaScript внутри, вы даете команду Node.js запустить этот файл вместо запуска нового интерактивного сеанса.

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

#!/usr/bin/env node
console.log('hello world');

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

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

$ chmod +x hello.js
$ ./hello.js
hello world

Путь к созданию полноценных веб-приложений с помощью Node.js долог и извилист, но таков же путь к написанию Django или Flask приложения на Python.

Иностранный язык

Иногда средой выполнения для JavaScript может быть другой язык программирования. Это типично для скриптовых языков в целом. Например, Python широко используется при разработке плагинов. Вы найдете его в редакторе Sublime Text, GIMP и Blender.

 

В качестве примера, вы можете оценить код JavaScript в Java-программе, используя API сценариев:

package org.example;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class App {

    public static void main(String[] args) throws ScriptException {

        final ScriptEngineManager manager = new ScriptEngineManager();
        final ScriptEngine engine = manager.getEngineByName("javascript");

        System.out.println(engine.eval("2 + 2"));
    }
}

Это расширение Java, хотя оно может быть недоступно в вашей конкретной виртуальной машине Java. Последующие поколения Java включают альтернативные скриптовые движки, такие как Rhino, Nashorn и GraalVM.

Чем это полезно?

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

JavaScript против Python

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

Варианты использования

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

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

JavaScript, с другой стороны, возник исключительно как язык сценариев на стороне клиента, предназначенный для того, чтобы сделать HTML-документы немного более интерактивными. Он намеренно прост и имеет особую направленность: добавление поведения к пользовательским интерфейсам. Это актуально и сегодня, несмотря на улучшенные возможности. С помощью Javascript вы можете создавать не только веб-приложения, но и настольные программы и мобильные приложения. Специально разработанные среды выполнения позволяют выполнять JavaScript на сервере или даже на устройствах Интернета вещей.

Философия

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

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

Версии

До недавнего времени вы могли найти две в значительной степени несовместимые версии Python, доступные для скачивания на его официальном веб-сайте. Это различие между Python 2.7 и Python 3.x сбивало с толку новичков и было основным фактором, замедлявшим внедрение последней ветки разработки.

В январе 2020 года, после нескольких лет затягивания сроков, поддержка Python 2.7 была, наконец, прекращена. Однако, несмотря на надвигающуюся нехватку обновлений для системы безопасности и предупреждений, издаваемых некоторыми правительственными учреждениями, все еще существует множество проектов, которые еще не были перенесены:

Timeline of JavaScript and Python Versions Хронология версий JavaScript и Python

Брендан Эйч создал JavaScript в 1995 году, но известный нам сегодня ECMAScript был стандартизирован двумя годами позже. С тех пор было выпущено всего несколько релизов, что выглядит незначительным по сравнению с множеством новых версий Python, выпускаемых каждый год за тот же период.

Обратите внимание на разрыв между ES3 и ES5, который длился целое десятилетие! Из-за политических конфликтов и разногласий в техническом комитете, ES4 так и не появился в веб-браузерах, но был использован Macromedia (позже Adobe) в качестве основы для ActionScript.

Первый серьезный пересмотр JavaScript произошел в 2015 году с появлением ES6, также известного как ES2015 или ECMAScript Harmony. В нем появилось много новых синтаксических конструкций, которые сделали язык более зрелым, безопасным и удобным для программиста. Это также ознаменовало поворотный момент в графике выпуска ECMAScript, который теперь обещает выпускать новую версию каждый год.

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

Время выполнения

Чтобы запустить программу на Python, вам сначала необходимо загрузить, установить и, возможно, настроить ее интерпретатор для вашей платформы. Некоторые операционные системы предоставляют готовый интерпретатор, но это может быть не та версия, которую вы хотите использовать. Существуют альтернативные реализации Python, включая CPython, PyPy, Jython, IronPython или Stackless Python. Вы также можете выбрать один из нескольких дистрибутивов Python , таких как Anaconda, которые поставляются с предустановленными пакетами сторонних производителей.

JavaScript отличается от других языков. Здесь нет отдельной программы для загрузки. Вместо этого каждый крупный веб-браузер поставляется с каким-либо движком JavaScript и API, которые вместе образуют среду выполнения. В предыдущем разделе вы узнали о программе Node.js, которая позволяет запускать JavaScript-код вне браузера. Вы также знаете о возможности встраивания JavaScript в другие языки программирования.

Экосистема

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

В прежние времена для написания JavaScript не требовалось ничего, кроме хорошего редактора кода. Вы бы скачали несколько библиотек, таких как jQuery, Underscore.js , или Backbone.js , или полагаться на Сеть доставки контента (CDN) для предоставления их вашим клиентам. Сегодня количество вопросов, на которые вам необходимо ответить, и инструментов, которые вам необходимо приобрести, чтобы начать создавать даже самый простой веб-сайт, могут быть пугающими.

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

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

$ npx create-react-app todo

На момент написания этой статьи выполнение этой команды заняло несколько минут, и было установлено целых 166 МБ в 1815 пакетах! Сравните это с запуском проекта Django на Python, который выполняется мгновенно:

$ django-admin startproject blog

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

  Python JavaScript
Code Editor / IDE PyCharm, VS Code Atom, VS Code, WebStorm
Code Formatter black Prettier
Dependency Manager Pipenv, poetry bower (deprecated), npm, yarn
Documentation Tool Sphinx JSDoc, sphinx-js
Interpreter bpython, ipython, python node
Library requests, dateutil axios, moment
Linter flake8, pyflakes, pylint eslint, tslint
Package Manager pip, twine bower (deprecated), npm, yarn
Package Registry PyPI npm
Package Runner pipx npx
Runtime Manager pyenv nvm
Scaffolding Tool cookiecutter cookiecutter, Yeoman
Test Framework doctest, nose, pytest Jasmine, Jest, Mocha
Web Framework Django, Flask, Tornado Angular, React, Vue.js

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

Иногда нет прямой аналогии между Python и JavaScript. Например, хотя вы, возможно, привыкли создавать изолированные виртуальные среды для своих проектов на Python, Node.js с этим можно справиться "из коробки", установив зависимости в локальную папку.

И наоборот, для проектов на JavaScript могут потребоваться дополнительные инструменты, уникальные для front-end разработки. Одним из таких инструментов является Babel, который преобразует ваш код в соответствии с различными плагинами, сгруппированными в пресеты. Он может работать с экспериментальными функциями ECMAScript, а также с TypeScript и даже с синтаксисом расширения React JSX.

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

Во время разработки вы хотите разбить свой код на многоразовые, тестируемые и автономные модули. Это разумно для опытного программиста на Python. К сожалению, изначально JavaScript не поддерживал модульность. Для этого по-прежнему требуется использовать отдельный инструмент, хотя это требование меняется. Популярными вариантами компоновки модулей являются webpack, Parcel и Browserify, которые также могут обрабатывать статические ресурсы .

Тогда у вас есть инструменты автоматизации сборки, такие как Grunt и gulp. Они отдаленно похожи на Fabric и Ansible в Python, хотя и используются локально. Эти инструменты автоматизируют такие скучные задачи, как копирование файлов или запуск транспилятора.

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

Как вы можете видеть, изучение экосистемы JavaScript - это бесконечное путешествие.

Модель памяти

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

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

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

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

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

Система типов JavaScript

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

Проверка типа

И Python, и JavaScript являются динамически типизированными, поскольку они проверяют типы во время выполнения, когда приложение выполняется, а не во время компиляции. Это удобно, потому что вам не приходится объявлять тип переменной, такой как int или str:

>>> data = 42
>>> data = 'This is a string'

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

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

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

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

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

data: str = 'This is a string'

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

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

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

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

>>> '3' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

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

>>> int('3') + 2
5

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

>>> '3' + str(2)
>>> '32'

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

В том же примере, что и ранее, JavaScript неявно преобразует числа в строки, когда вы используете оператор plus (+):

> '3' + 2
'32'

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

> '3' - 2
1

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

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

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

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

Типы JavaScript

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

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

Это единственные примитивные типы, доступные в JavaScript:

  • boolean
  • null
  • number
  • string
  • symbol ( начиная с ES6)
  • undefined

С другой стороны, вот несколько ссылочных типов, которые поставляются с готовым JavaScript:

  • Array
  • Boolean
  • Date
  • Map
  • Number
  • Object
  • RegExp
  • Set
  • String
  • Symbol
  • (…)

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

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

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

> 'Lorem ipsum'.length
11

Несмотря на то, что строковый литерал в JavaScript является примитивным типом данных, вы можете проверить его атрибут .length. Что происходит под капотом, так это то, что ваш код заменяется вызовом конструктора String объекта :

> new String('Lorem ipsum').length
11

Конструктор - это специальная функция, которая создает новый экземпляр заданного типа. Вы можете видеть, что атрибут .length определяется объектом String. Этот механизм упаковки известен как автобоксирование и был скопирован непосредственно из языка программирования Java.

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

> x = 42
> y = x
> x++  // This is short for x += 1
> console.log(x, y)
43 42

Присваивание y = x создает новое значение в памяти. Теперь у вас есть две разные копии числа 42, на которые ссылаются x и y, поэтому увеличение одной из них не влияет на другую.

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

> x = {name: 'Person1'}
> y = x
> x.name = 'Person2'
> console.log(y)
{name: 'Person2'}

Object это ссылочный тип в JavaScript. Здесь у вас есть две переменные, x и y, ссылающиеся на один и тот же экземпляр объекта Person. Изменение, внесенное в одну из переменных, отражается в другой переменной.

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

Примечание: Справедливости ради, это почти идентично тому, как Python обрабатывает передачу объектов, несмотря на отсутствие примитивных типов. Изменяемые типы, такие как list и dict, не создают копий, в то время как неизменяемые типы, такие как int и str, создают.

Чтобы проверить, является ли переменная примитивным типом или ссылочным типом в JavaScript, вы можете использовать встроенный оператор typeof:

> typeof 'Lorem ipsum'
'string'
> typeof new String('Lorem ipsum')
'object'

Для ссылочных типов оператор typeof всегда возвращает общую строку "object".

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

> typeof noSuchVariable === 'undefined'
true
> noSuchVariable === undefined
ReferenceError: noSuchVariable is not defined

Сравнение несуществующей переменной с любым значением приведет к возникновению исключения!

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

> today = new Date()
> today.constructor.name
'Date'
> today instanceof Date
true
> Date.prototype.isPrototypeOf(today)
true

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

Иерархия типов

Python и JavaScript - это объектно-ориентированные языки программирования. Они

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

Примечание: Класс - это шаблон для объектов. Вы можете думать о классах как о формочках для печенья или фабриках объектов.

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

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

Суть этой истории в том, что в JavaScript нет классов. Ну, технически, вы можете использовать ключевое слово class, которое было введено в ES6, но это просто синтаксический сахар , чтобы облегчить работу новичкам. Прототипы все еще используются за кулисами, поэтому стоит познакомиться с ними поближе, что у вас будет возможность сделать позже.

Тип функции

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

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

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

let countdown = 5;
const id = setInterval(function() {
  if (countdown > 0) {
    console.log(`${countdown--}...`);
  } else if (countdown === 0) {
    console.log('Go!');
    clearInterval(id);
  }
}, 1000);

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

Синтаксис JavaScript

JavaScript и Python - это языки сценариев высокого уровня, которые имеют много общего в синтаксисе. Особенно это касается их последних версий. Тем не менее, JavaScript был разработан так, чтобы напоминать Java, в то время как Python был создан по образцу языков ABC и Modula-3.

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

function fib(n)
{
  if (n > 1) {
    return fib(n-2) + fib(n-1);
  }
  return 1;
}

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

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

Примечание: Вы могли бы упростить текст функции, описанный выше, воспользовавшись преимуществами , если (?:), которую иногда называют Оператором Элвиса, потому что она похожа на прическу знаменитого певца:

return (n > 1) ? fib(n-2) + fib(n-1) : 1;

Это эквивалентно условному выражению в Python.

Говоря об отступах, обычно код JavaScript форматируется с использованием двух пробелов на уровень отступа вместо рекомендуемых четырех в Python.

Утверждения

Чтобы уменьшить трудности для тех, кто переходит с Java или другого языка программирования семейства C, JavaScript завершает инструкции знакомой точкой с запятой (;). Если вы когда-либо программировали на одном из этих языков, то знаете, что точка с запятой после команды активизирует мышечную память:

alert('hello world');

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

Примечание: В Python также можно использовать точки с запятой! Хотя они не очень популярны, они помогают выделить несколько операторов в одной строке:

import pdb; pdb.set_trace()

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

Идентификаторы

Идентификаторы, такие как имена переменных или функций, должны быть буквенно-цифровыми в JavaScript и Python. Другими словами, они могут содержать только буквы, цифры и несколько специальных символов. В то же время они не могут начинаться с цифры. Хотя нелатинские символы разрешены, их, как правило, следует избегать:

  • Юридический: foo, foo42, _foo, $foo, fößar
  • Незаконный: 42foo

Имена на обоих языках чувствительны к регистру, поэтому такие переменные, как foo и Foo, различны. Тем не менее, соглашения об именовании в JavaScript немного отличаются от соглашений в Python:

  Python JavaScript
Type ProjectMember ProjectMember
Variable, Attribute, or Function first_name firstName

В общем, Python рекомендует использовать lower_case_with_underscores, также известный как snake_case для составных имен, чтобы отдельные слова разделялись символом подчеркивания (_). Единственным исключением из этого правила являются классы, имена которых должны быть написаны с заглавной буквы в словах или в регистре Pascal. JavaScript также использует заглавные слова для типов, но для всего остального использует mixedCase или нижний регистр.

Комментарии

В JavaScript есть как однострочные, так и многострочные комментарии:

x++;  // This is a single-line comment

/*
 This whole paragraph
 is a comment and will
 be ignored.
*/

Вы можете начать комментарий в любом месте строки с двойной косой черты (//), которая похожа на знак хэша в Python (#). Хотя в Python нет многострочных комментариев, вы можете имитировать их, заключив фрагмент кода в тройные кавычки ('''), чтобы создать многострочную строку . В качестве альтернативы, вы можете обернуть это в if оператор, который никогда не вычисляется как True:

if False:
    ...

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

Строковые литералы

Для определения строковых литералов в JavaScript вы можете использовать пару одинарных кавычек (') или двойных кавычек (") взаимозаменяемо, как и в Python. Однако долгое время не существовало способа определить многострочные строки в JavaScript. Только в ES6 в 2015 году появились шаблонных литерала, которые выглядят как гибрид f-строк и многострочных строк, заимствованных из Python:

var name = 'John Doe';
var message = `Hi ${name.split(' ')[0]},

We're writing to you regarding...

Kind regards,
Xyz
`;

Шаблон начинается с обратного тика (`), также известного как серьезное ударение, вместо обычных кавычек. Чтобы интерполировать переменную или любое допустимое выражение, вы должны использовать знак доллара, за которым следует пара соответствующих фигурных скобок: ${...}. Это отличается от f-строк Python, которые не требуют знака доллара.

Области действия переменных

Когда вы определяете переменную в JavaScript так же, как обычно делаете в Python, вы неявно создаете глобальную переменную. Поскольку глобальные переменные нарушают инкапсуляцию, они редко должны вам понадобиться! Правильным способом объявления переменных в JavaScript всегда было использование ключевого слова var:

x = 42;     // This is a global variable. Did you really mean that?
var y = 15; // This is global only when declared in a global context.

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

> let name = 'John Doe';
> const PI = 3.14;
> PI = 3.1415;
TypeError: Assignment to constant variable.

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

let name;
name = 'John Doe';

Когда вы не указываете начальное значение, вы создаете то, что называется объявлением переменной , а не определением переменной . Таким переменным автоматически присваивается специальное значение undefined, которое является одним из примитивных типов в JavaScript. В Python все по-другому, где вы всегда определяете переменные, за исключением аннотаций переменных. Но даже в этом случае эти переменные технически не объявлены:

name: str
name = 'John Doe'

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

Инструкции переключения

Если вы жаловались на то, что в Python нет надлежащего оператора switch, то вы будете рады узнать, что JavaScript выполняет:

// As with C, clauses will fall through unless you break out of them.
switch (expression) {
  case 'kilo':
    value = bytes / 2**10;
    break;
  case 'mega':
    value = bytes / 2**20;
    break;
  case 'giga':
    value = bytes / 2**30;
    break;
  default:
    console.log(`Unknown unit: "${expression}"`);
}

Выражение может быть преобразовано в любой тип, включая строку, что не всегда имело место в старых версиях Java, которые повлияли на JavaScript. Кстати, вы заметили знакомый оператор возведения в степень (**) в приведенном выше фрагменте кода? Он не был доступен на JavaScript до выхода ES7 в 2016 году.

Перечисления

В чистом JavaScript нет встроенного типа перечисления , но вы можете использовать тип enum в TypeScript или эмулировать его с помощью чего-то подобного этому:

const Sauce = Object.freeze({
  BBQ: Symbol('bbq'),
  CHILI: Symbol('chili'),
  GARLIC: Symbol('garlic'),
  KETCHUP: Symbol('ketchup'),
  MUSTARD: Symbol('mustard')
});

Замораживание объекта не позволяет добавлять или удалять его атрибуты. Это отличается от константы, которая может быть изменяемой! Константа всегда будет указывать на один и тот же объект, но сам объект может изменять свое значение:

> const fruits = ['apple', 'banana'];
> fruits.push('orange'); // ['apple', 'banana', 'orange']
> fruits = [];
TypeError: Assignment to constant variable.

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

Функции стрелок

До ES6 вы могли определять только функцию или анонимное функциональное выражение, используя ключевое слово function:

function add(a, b) {
  return a + b;
}

let add = function(a, b) {
  return a + b;
};

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

let add = (a, b) => a + b;

Обратите внимание, что ключевого слова function больше нет, а оператор return является неявным. Символ стрелки (=>) отделяет аргументы функции от ее тела. Люди иногда называют это функцией толстой стрелки, потому что изначально она была заимствована из CoffeeScript, у которого также есть аналог тонкой стрелки (->).

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

let add = (a, b) => {
  const result = a + b;
  return result;
}

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

let add = (a, b) => ({
  result: a + b
});

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

Аргументы по умолчанию

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

> function greet(name = 'John') {
…   console.log('Hello', name);
… }
> greet();
Hello John

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

> function foo(a, b=a+1, c=[]) {
…   c.push(a);
…   c.push(b);
…   console.log(c);
… }
> foo(1);
[1, 2]
> foo(5);
[5, 6]

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

Переменные функции

Когда вы хотите объявить функцию с переменным числом параметров в Python, вы используете специальный синтаксис *args. Эквивалентом JavaScript был бы параметр rest, определенный с помощью оператора spread (...):

> function average(...numbers) {
…   if (numbers.length > 0) {
…     const sum = numbers.reduce((a, x) => a + x);
…     return sum / numbers.length;
…   }
…   return 0;
… }
> average();
0
> average(1);
1
> average(1, 2);
1.5
> average(1, 2, 3);
2

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

const redFruits = ['apple', 'cherry'];
const fruits = ['banana', ...redFruits];

В зависимости от того, где вы размещаете оператор spread в целевом списке, вы можете добавлять предваряющие элементы или вставлять их где-то посередине.

Задания на деструкцию

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

> const fruits = ['apple', 'banana', 'orange'];
> const [a, b, c] = fruits;
> console.log(b);
banana

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

const person = {name: 'John Doe', age: 42, married: true};
const {name: fullName, age} = person;
console.log(`${fullName} is ${age} years old.`);

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

with Утверждения

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

const person = {name: 'John Doe', age: 42, married: true};
with (person) {
  console.log(`${name} is ${age} years old.`);
}

Это работает как конструкция в Object Pascal, в которой локальная область временно дополняется атрибутами данного объекта.

Примечание: Операторы with в Python и JavaScript являются ложными друзьями. В Python вы используете инструкцию with для управления ресурсами с помощью контекстных менеджеров .

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

Повторяемые объекты, итераторы и генераторы

Начиная с ES6, JavaScript имеет итерационные и итераторные протоколы, а также функции генератора, которые выглядят почти идентично итерациям Python, итераторам и генераторам. Чтобы превратить обычную функцию в функцию-генератор, вам нужно добавить звездочку (*) после ключевого слова function:

function* makeGenerator() {}
Однако

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

Когда вы вызываете функцию-генератор, она не выполняет тело этой функции. Вместо этого она возвращает приостановленный объект-генератор, который соответствует протоколу итератора. Чтобы продвинуть свой генератор, вы можете вызвать .next(), который аналогичен встроенному в Python next():

> const generator = makeGenerator();
> const {value, done} = generator.next();
> console.log(value);
undefined
> console.log(done);
true

В результате вы всегда будете получать объект status с двумя атрибутами: последующим значением и флагом, указывающим, исчерпан ли генератор. Python выдает исключение StopIteration, когда в генераторе больше нет значений.

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

let shouldStopImmediately = false;

function* randomNumberGenerator(maxTries=3) {
  let tries = 0;
  while (tries++ < maxTries) {
    if (shouldStopImmediately) {
      return 42; // The value is optional
    }
    yield Math.random();
  }
}

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

Эквивалентом выражения yield from в Python, которое делегирует итерацию другому итератору или итерируемому объекту, является выражение yield*:

> function* makeGenerator() {
…   yield 1;
…   yield* [2, 3, 4];
…   yield 5;
… }
> const generator = makeGenerator()
> generator.next();
{value: 1, done: false}
> generator.next();
{value: 2, done: false}
> generator.next();
{value: 3, done: false}
> generator.next();
{value: 4, done: false}
> generator.next();
{value: 5, done: false}
> generator.next();
{value: undefined, done: true}

Интересно, что законно использовать return и yield одновременно:

function* makeGenerator() {
  return yield 42;
}

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

def make_generator():
    return (yield 42)

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

function* makeGenerator() {
  const message = yield 42;
  return message;
}

Если вы знаете сопрограммы в Python, то вы помните, что объекты-генераторы могут быть как производителями, так и потребители. Вы можете отправить произвольные значения в приостановленный генератор, указав необязательный аргумент для .next():

> function* makeGenerator() {
…   const message = yield 'ping';
…   return message;
… }
> const generator = makeGenerator();
> generator.next();
{value: "ping", done: false}
> generator.next('pong');
{value: "pong", done: true}

Первый вызов .next() запускает генератор до тех пор, пока не будет получено первое выражение yield, которое возвращает "ping". Второй вызов передает "pong", который сохраняется в константе и немедленно возвращается из генератора.

Асинхронные функции

Рассмотренный выше изящный механизм послужил основой для асинхронного программирования и внедрения ключевых слов async и await в Python. JavaScript пошел по тому же пути, внедрив асинхронных функций в ES8 в 2017 году.

В то время как функция-генератор возвращает особый тип итератора, объект-генератор, асинхронные функции всегда возвращают обещание, которое было первым введен в ES6. Обещание представляет собой будущий результат асинхронного вызова, такого как fetch() из Fetch API.

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

async function greet(name) {
  return `Hello ${name}`;
}

async function main() {
  const promise = greet('John');
  const greeting = await promise;
  console.log(greeting); // "Hello John"
}

main();

Обычно вы используете await и присваиваете результат за один раз:

const greeting = await greet('John');

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

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

Объекты и конструкторы

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

let person = {
  name: 'John Doe',
  age: 42,
  married: true
};

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

> person.age++;
> person['age'];
43

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

> let person = {
…   'full name': 'John Doe'
… };
> person['full name'];
'John Doe'
> person.full name;
SyntaxError: Unexpected identifier

Подобно словарю и некоторым объектам в Python, объекты в JavaScript имеют динамические атрибуты. Это означает, что вы можете добавлять новые атрибуты к объекту или удалять существующие:

> let person = {name: 'John Doe'};
> person.age = 42;
> console.log(person);
{name: "John Doe", age: 42}
> delete person.name;
true
> console.log(person);
{age: 42}

Начиная с ES6, объекты могут иметь атрибуты с вычисляемыми именами :

> let person = {
…   ['full' + 'Name']: 'John Doe'
… };
> person.fullName;
'John Doe'

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

> let person = {
…   name: 'John Doe',
…   sayHi: function() {
…     console.log(`Hi, my name is ${person.name}.`);
…  }
… };
> person.sayHi();
Hi, my name is John Doe.

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

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

> let jdoe = {
…   name: 'John Doe',
…   sayHi: function() {
…     console.log(`Hi, my name is ${this.name}.`);
…   }
… };
> jdoe.sayHi();
Hi, my name is John Doe.

После замены жестко заданного person на this, что аналогично self в Python, не будет иметь значения, каково имя переменной, и результат будет тем же как и раньше.

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

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

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

function Person() {
  console.log('Calling the constructor');
}

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

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

> Person();
Calling the constructor
undefined

Что делает его особенным, так это то, как вы его называете:

> new Person();
Calling the constructor
Person {}

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

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

function Person(name) {
  this.name = name;
  this.sayHi = function() {
    console.log(`Hi, my name is ${this.name}.`);
  }
}

Теперь вы можете создать несколько различных Person сущностей:

const jdoe = new Person('John Doe');
const jsmith = new Person('John Smith');

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

> const jdoe = new Person('John Doe');
> const jsmith = new Person('John Smith');
> jsmith.sayHi = _ => console.log('What?');
> jdoe.sayHi();
Hi, my name is John Doe.
> jsmith.sayHi();
What?

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

Прототипы

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

function Person(name) {
  this.name = name;
}

Person.prototype.sayHi = function() {
  console.log(`Hi, my name is ${this.name}.`);
};

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

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

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

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

> Person.prototype.PI = 3.14;
> new Person('John Doe').PI;
3.14
> new Person('John Smith').PI;
3.14

Чтобы проиллюстрировать возможности прототипов, вы можете попытаться расширить поведение существующих объектов или даже встроенного типа данных. Давайте добавим новый метод к типу string в JavaScript, указав его в объекте-прототипе:

String.prototype.toSnakeCase = function() {
  return this.replace(/\s+/g, '')
             .split(/(?<=[a-z])(?=[A-Z])/g)
             .map(x => x.toLowerCase())
             .join('_');
};

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

> "loremIpsumDolorSit".toSnakeCase();
'lorem_ipsum_dolor_sit'

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

Классы

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

class Person {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    console.log(`Hi, my name is ${this.name}.`);
  }
}

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

В вашем классе могут быть геттеры и сеттеры, которые аналогичны классу Python свойства:

> class Square {
…   constructor(size) {
…     this.size = size; // Triggers the setter
…   }
…   set size(value) {
…     this._size = value; // Sets the private field
…   }
…   get area() {
…     return this._size**2;
…   }
… }
> const box = new Square(3);
> console.log(box.area);
9
> box.size = 5;
> console.log(box.area);
25

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

Распространенным шаблоном для инкапсуляции внутренней реализации в JavaScript является Немедленно вызываемое функциональное выражение (IIFE), которое может выглядеть следующим образом:

> const odometer = (function(initial) {
…   let mileage = initial;
…   return {
…     get: function() { return mileage; },
…     put: function(miles) { mileage += miles; }
…   };
… })(33000);
> odometer.put(65);
> odometer.put(12);
> odometer.get();
33077

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

const odometer = ((initial) => {
  let mileage = initial;
  return {
    get: _ => mileage,
    put: (miles) => mileage += miles
  };
})(33000);

Именно так JavaScript исторически эмулировал модули, чтобы избежать коллизий имен в глобальном пространстве имен. Без IIFE, который использует замыкания и область действия функции для предоставления только ограниченного общедоступного API, все было бы доступно из вызывающего кода.

Иногда вы хотите определить фабрику или служебную функцию, которая логически принадлежит вашему классу. В Python у вас есть @classmethod и @staticmethod декораторы, которые позволяют вам связывать статические методы с классом. Чтобы добиться такого же результата в JavaScript, вам нужно использовать модификатор метода static:

class Color {
  static brown() {
    return new Color(244, 164, 96);
  }
  static mix(color1, color2) {
    return new Color(...color1.channels.map(
      (x, i) => (x + color2.channels[i]) / 2
    ));
  }
  constructor(r, g, b) {
    this.channels = [r, g, b];
  }
}
const color1 = Color.brown();
const color2 = new Color(128, 0, 128);
const blended = Color.mix(color1, color2);

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

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

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
  fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

class Gentleman extends Person {
  signature() {
    return 'Mr. ' + super.fullName()
  }
}

В Python вы могли бы расширить более одного класса, но в JavaScript это невозможно. Чтобы ссылаться на атрибуты из родительского класса, вы можете использовать super(),, который должен быть вызван в конструкторе для передачи аргументов вверх по цепочке .

Декораторы

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

Несколько фреймворков уже используют пользовательский синтаксис для декораторов, который необходимо преобразовать в обычный JavaScript. Если вы выберете вариант TC-39, то вы сможете оформлять только классы и их членов. Похоже, что в JavaScript не будет какого-либо специального синтаксиса для оформления функций.

Особенности JavaScript

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

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

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

Фиктивный массив

Списки и кортежи в Python реализованы в виде массивов в традиционном смысле, тогда как в JavaScript Array тип имеет больше общего со словарем Python . Тогда что же такое массив?

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

В Python список - это массив указателей, которые обычно являются целыми числами и ссылаются на разнородные объекты, разбросанные в различных областях памяти.

Примечание: Для низкоуровневых массивов в Python вам может быть интересно ознакомиться со встроенным модулем array.

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

Когда вы удаляете элемент из массива в JavaScript, вы создаете пробел:

> const fruits = ['apple', 'banana', 'orange'];
> delete fruits[1];
true
> console.log(fruits);
['apple', empty, 'orange']
> fruits[1];
undefined

Размер массива не меняется после удаления одного из его элементов:

> console.log(fruits.length);
3

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

> fruits[10] = 'watermelon';
> console.log(fruits.length);
11
> console.log(fruits);
['apple', empty, 'orange', empty × 7, 'watermelon']

В Python это не сработало бы.

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

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

>>> sorted([53, 2020, 42, 1918, 7])
[7, 42, 53, 1918, 2020]

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

>>> sorted(['lorem', 'ipsum', 'dolor', 'sit', 'amet'])
['amet', 'dolor', 'ipsum', 'lorem', 'sit']

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

>>> sorted([42, 'not a number'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'

Теперь вы знаете, что Python - язык со строгой типизацией, и ему не нравится смешивать типы. С JavaScript, напротив, все наоборот. Он охотно преобразует элементы несовместимых типов в соответствии с некоторыми неясными правилами.

Вы можете использовать .sort() для выполнения сортировки в JavaScript:

> ['lorem', 'ipsum', 'dolor', 'sit', 'amet'].sort();
['amet', 'dolor', 'ipsum', 'lorem', 'sit']

Оказывается, сортировка строк работает так, как ожидалось. Давайте посмотрим, как это работает с числами:

> [53, 2020, 42, 1918, 7].sort();
[1918, 2020, 42, 53, 7]

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

> [53, 2020, 42, 1918, 7].sort((a, b) => a - b);
[7, 42, 53, 1918, 2020]

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

  1. Ноль, когда два элемента равны
  2. Положительное число, когда элементы необходимо поменять местами
  3. Отрицательное число, когда элементы расположены в правильном порядке

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

Автоматическая вставка точки с запятой

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

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

function makePerson(name) {
  return
    ({
      fullName: name,
      createdAt: new Date()
    })
}

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

> const jdoe = makePerson('John Doe');
> console.log(jdoe);
undefined

Ваша функция изменила предполагаемое действие, вернув undefined, потому что были вставлены две точки с запятой вместо одной:

function makePerson(name) {
  return;
    ({
      fullName: name,
      createdAt: new Date()
    });
}

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

Чтобы исправить этот пример, вам нужно изменить форматирование кода таким образом, чтобы возвращаемое значение начиналось с той же строки, что и оператор return:

function makePerson(name) {
  return {
    fullName: name,
    createdAt: new Date()
  };
}

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

const total = 2 + 3
(4 + 5).toString()

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

const total = 2 + 3(4 + 5).toString();

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

Запутывающие циклы

Циклы в JavaScript особенно запутанны, потому что их очень много и они выглядят одинаково, в то время как в Python их всего два. Основным типом цикла в JavaScript является for цикл, который был перенесен из Java:

const fruits = ['apple', 'banana', 'orange'];
for (let i = 0; i < fruits.length; i++) {
  console.log(fruits[i]);
}

Он состоит из трех частей, каждая из которых необязательна:

  1. Инициализация: let i = 0
  2. Условие: i < fruits.length
  3. Очистка: i++

Первая часть выполняется только один раз перед запуском цикла и обычно устанавливает начальное значение для счетчика. Затем, после каждой итерации, выполняется часть очистки для обновления счетчика. Сразу после этого вычисляется условие, чтобы определить, следует ли продолжать цикл. Это примерно эквивалентно перебору списка индексов в Python:

fruits = ['apple', 'banana', 'orange']
for i in range(len(fruits)):
    print(fruits[i])

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

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

for (;;) {
  // An infinite loop
}

Однако более идиоматичным способом выполнения такой итерации был бы цикл while, который очень похож на тот, который вы найдете в Python:

while (true) {
  const age = prompt('How old are you?');
  if (age >= 18) {
    break;
  }
}

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

let age;
do {
  age = prompt('How old are you?');
} while (age < 18);

Помимо остановки итерации на полпути с помощью ключевого слова break, вы можете перейти к следующей итерации, используя ключевое слово continue, как в Python:

for (let i = 0; i < 10; i++) {
  if (i % 2 === 0) {
    continue;
  }
  console.log(i);
}

Однако, чего вы не можете сделать, так это использовать предложение else для циклов.

У вас может возникнуть соблазн попробовать цикл for...in в JavaScript, полагая, что он будет выполнять итерацию по значениям подобно циклу Python for. Хотя он выглядит похожим и имеет похожее название, на самом деле он ведет себя совсем по-другому!

Цикл for...in в JavaScript выполняет итерацию по атрибутам данного объекта, включая те, которые находятся в цепочке прототипов:

> const object = {name: 'John Doe', age: 42};
> for (const attribute in object) {
…   console.log(`${attribute} = ${object[attribute]}`);
… }
name = John Doe
age = 42

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

Когда вы вводите в цикл for...in массив, он выполняет итерацию по числовым индексам массива. Как вы уже знаете, массивы в JavaScript - это просто расширенные словари:

> const fruits = ['apple', 'banana', 'orange'];
… for (const fruit in fruits) {
…   console.log(fruit);
… }
0
1
2

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

const fruits = ['apple', 'banana', 'orange'];
fruits.forEach(fruit => console.log(fruit));

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

Примечание: Чтобы проверить, определен ли в объекте один атрибут, используйте оператор in:

> 'toString' in [1, 2, 3];
true
> '__str__' in [1, 2, 3];
false

Наконец, когда в спецификации ES6 были представлены протоколы iterable и iterator, это позволило реализовать долгожданный цикл, который будет выполнять итерации по последовательностям. Однако, поскольку название for...in уже было занято, им пришлось придумать другое.

Цикл for...of наиболее близок к циклу for в Python. С его помощью вы можете выполнять итерацию по любому итерируемому объекту, включая строки и массивы:

const fruits = ['apple', 'banana', 'orange'];
for (const fruit of fruits) {
  console.log(fruit);
}

Это, вероятно, самый интуитивно понятный способ для программиста на Python выполнять итерации в JavaScript.

Конструктор без new

Давайте вернемся к типу Person, определенному ранее:

function Person(name) {
  this.name = name;
  this.sayHi = function() {
    console.log(`Hi, my name is ${this.name}.`);
  }
}

Если вы забудете правильно вызвать этот конструктор, указав перед ним ключевое слово new, то он автоматически завершится ошибкой и оставит вас с переменной undefined:

> let bob = Person('Bob');
> console.log(bob);
undefined

Есть хитрость, чтобы защитить себя от этой ошибки. Если вы опустите ключевое слово new, не будет никакого объекта для привязки, поэтому переменная this внутри конструктора будет указывать на глобальный объект , такой как window объект в веб-браузере. Вы можете обнаружить это и делегировать действительный вызов конструктора:

> function Person(name) {
…   if (this === window) {
…     return new Person(name);
…   }
…   this.name = name;
…   this.sayHi = function() {
…     console.log(`Hi, my name is ${this.name}.`);
…   }
… }
> let person = Person('John Doe');
> console.log(person);
Person {name: 'John Doe', sayHi: ƒ}

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

Примечание: Тройной знак равенства (===) установлен намеренно и связан со слабой типизацией в JavaScript. Вы узнаете больше об этом ниже.

Глобальная область видимости по умолчанию

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

  • var
  • let
  • const

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

> function call() {
…   global = 42;
…   let local = 3.14
… }
> call();
> console.log(global);
42
> console.log(local);
ReferenceError: local is not defined

Интересно, что правила, определяющие, объявляете ли вы локальную или глобальную переменную в Python, намного сложнее, чем это. В Python также существуют другие виды области видимости переменных.

Область действия функции

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

Независимо от того, насколько глубоко в функции определена переменная, ее область действия будет распространяться на всю функцию:

> function call() {
…   if (true) {
…     for (let i = 0; i < 10; i++) {
…       var notGlobalNorLocal = 42 + i;
…     }
…   }
…   notGlobalNorLocal--;
…   console.log(notGlobalNorLocal);
… }
> call();
50

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

> function call() {
…   function inner() {
…     var notGlobalNorLocal = 42;
…   }
…   inner();
…   console.log(notGlobalNorLocal);
… }
> call();
ReferenceError: notGlobalNorLocal is not defined

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

Подъем

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

Давайте начнем с маленькой загадки:

var x = 42;

function call() {
  console.log(x); // A = ???
  var x = 24;
  console.log(x); // B = ???
}

call();
console.log(x); // C = ???

Подумайте, каков будет результат:

  1. A = 42, B = 24, C = 42
  2. A = 42, B = 24, C = 24
  3. A = 24, B = 24, C = 42
  4. A = 24, B = 24, C = 24
  5. SyntaxError: Identifier 'x' has already been declared

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

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

Хорошо, готовы? Правильный ответ - ни один из вышеперечисленных! В результате будет выведено следующее:

  • A = undefined
  • B = 24
  • C = 42

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

var x = 42;

function call() {
  var x;
  console.log(x); // A = undefined
  x = 24;
  console.log(x); // B = 24
}

call();
console.log(x); // C = 42

Глобальная переменная временно маскируется локальной переменной, поскольку поиск по имени осуществляется извне. Объявление x внутри функции перемещается вверх. Когда переменная объявлена, но не инициализирована, она имеет значение undefined.

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

call(); // Prints "hello"

function call() {
  console.log('hello');
}

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

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

call(); // TypeError: call is not a function

var call = function() {
  console.log('hello');
};

В результате ваша переменная останется undefined до тех пор, пока вы ее не инициализируете.

Иллюзорные сигнатуры функций

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

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

> function currentYear() {
…   return new Date().getFullYear();
… }
> currentYear(42, 'foobar');
2020

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

> function truthy(expression) {
…   return !!expression;
… }
> truthy();
false

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

> function sum() {
…   return [...arguments].reduce((a, x) => a + x);
… }
> sum(1, 2, 3, 4);
10

arguments это объект, подобный массиву, который может быть повторен и имеет числовые индексы, но, к сожалению, он не поставляется с .forEach(). Чтобы обернуть его в массив, вы можете использовать оператор spread.

Раньше это был единственный способ определения переменных функций в JavaScript перед параметром rest в ES6.

Неявное приведение типа

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

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

if ('2' == 2) { // Evaluates to true

В общем, вам следует предпочесть оператор строгого сравнения (===), чтобы быть в безопасности:

> '2' === 2;
false
> '2' !== 2;
true

Этот оператор сравнивает как значения, так и типы их операндов.

Нет целочисленного типа

В Python есть несколько типов данных для представления чисел:

    В Python есть несколько
  • типов данныхint для представления чисел:<<<3>>
  • float
  • complex
>

В Python есть несколько longтипов данныхint для представления чисел:

В Python есть несколько типов данных для представления чисел:

В Python есть несколько Numberтипов данныхfloat для представления чисел:<<<3>>

>

В Python есть несколько типов данных для представления чисел:<<<3>>

>

В Python есть несколько типов данных для представления чисел:<<<3>>

>

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

> 0.1 + 0.2;
0.30000000000000004
Во-вторых, числа с плавающей запятой подвержены ошибке округления из-за того, как они представлены в памяти компьютера. Как таковые, они не подходят для приложений, требующих высокой точности , таких как денежные расчеты:

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

высокой точности , таких как денежные расчеты:
> const x = Number.MAX_SAFE_INTEGER + 1;
> const y = Number.MAX_SAFE_INTEGER + 2;
> x === y;
true
Во-вторых, числа с плавающей запятой подвержены ошибке округления из-за того, как они представлены в памяти компьютера. Как таковые, они не подходят для приложений, требующих высокой точности , таких как денежные расчеты:

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

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

Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.<<<3>>

>
> const x = BigInt(Number.MAX_SAFE_INTEGER) + 1n;
> const y = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
> x === y;
false
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

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

или читали какой-то случайный файл, не зная об этом.
> typeof 42;
'number'
> typeof 42n;
'bigint'
> typeof BigInt(42);
'bigint'
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

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

или читали какой-то случайный файл, не зная об этом.
    Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы
  1. не могли открыть файлBigInt64Array или читали какой-то случайный файл, не зная об этом.<<<3>>
  2. BigUint64Array
>

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

или читали какой-то случайный файл, не зная об этом.

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

Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файлNone или читали какой-то случайный файл, не зная об этом.<<<3>> null nil null undefined

>

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

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

не могли открыть файл

или читали какой-то случайный файл, не зная об этом.<<<3>>

>

Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы nullне могли открыть файлundefined или читали какой-то случайный файл, не зная об этом.<<<3>> null

>
let x; // undefined
let y = null;
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

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

или читали какой-то случайный файл, не зная об этом.
let z = undefined;
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

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

function fn(required, optional) {
  if (typeof optional === 'undefined') {
    optional = 'default';
  }
  // ...
}
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

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

или читали какой-то случайный файл, не зная об этом.

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

или читали какой-то случайный файл, не зная об этом.
fn(42);            // optional = "default"
fn(42, undefined); // optional = "default"
fn(42, null);      // optional = null
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы nullне могли открыть файлundefined или читали какой-то случайный файл, не зная об этом.<<<3>>

>
> foobar;
ReferenceError: foobar is not defined
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

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

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

или читали какой-то случайный файл, не зная об этом.

Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы selfне могли открыть файл или читали какой-то случайный файл, не зная об этом.<<<3>>

>

Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы thisне могли открыть файл или читали какой-то случайный файл, не зная об этом.<<<3>>

>

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

не могли открыть файл или читали какой-то случайный файл, не зная об этом.
> let jdoe = {
…   name: 'John Doe',
…   whoami: function() {
…     console.log(this);
…   }
… };
> jdoe.whoami();
{name: "John Doe", whoami: ƒ}
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

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

или читали какой-то случайный файл, не зная об этом.
> function whoami() {
…   console.log(this);
… }
> let jdoe = {name: 'John Doe', whoami};
> jdoe.whoami();
{name: "John Doe", whoami: ƒ}
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

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

не могли открыть файл или читали какой-то случайный файл, не зная об этом.
> jdoe.whoami();
{name: "John Doe", whoami: ƒ}
> whoami();
Window {…}
Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.

Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы whoami()не могли открыть файлjdoe или читали какой-то случайный файл, не зная об этом.<<<3>> jdoe this

>

Когда Node.js стал популярным, люди начали использовать его для написания серверных приложений. Им нужен был способ доступа к локальной файловой системе. Некоторые операционные системы идентифицируют файлы по произвольным целым числам. Иногда эти числа не имели точного представления в JavaScript, поэтому вы не могли открыть файл или читали какой-то случайный файл, не зная об этом.<<<3>> this

>

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

или читали какой-то случайный файл, не зная об этом.
> jdoe.whoami();
{name: "John Doe", whoami: ƒ}
> window.whoami();
Window {…}

Вы видите здесь закономерность?

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

Давайте определим другой объектный литерал, чтобы продемонстрировать это:

const collection = {
  items: ['apple', 'banana', 'orange'],
  type: 'fruit',
  show: function() {
    this.items.forEach(function(item) {
      console.log(`${item} is a ${this.type}`);
    });
  }
};

collection представляет собой набор элементов, имеющих общий тип. В настоящее время .show() работает не так, как ожидалось, поскольку не отображает тип элемента:

> collection.show();
apple is a undefined
banana is a undefined
orange is a undefined

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

function callback(item) {
  console.log(`${item} is a ${this.type}`);
}

const collection = {
  items: ['apple', 'banana', 'orange'],
  type: 'fruit',
  show: function() {
    this.items.forEach(callback);
  }
};

Самый простой способ обойти это - заменить this в обратном вызове пользовательской переменной или константой:

const collection = {
  items: ['apple', 'banana', 'orange'],
  type: 'fruit',
  show: function() {
    const that = this;
    this.items.forEach(function(item) {
      console.log(`${item} is a ${that.type}`);
    });
  }
};

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

Теперь результат правильный:

> collection.show();
apple is a fruit
banana is a fruit
orange is a fruit

Этот шаблон настолько распространен, что .forEach() принимает необязательный параметр для замены this в обратном вызове:

const collection = {
  items: ['apple', 'banana', 'orange'],
  type: 'fruit',
  show: function() {
    this.items.forEach(function(item) {
      console.log(`${item} is a ${this.type}`);
    }, this);
  }
};
collection.show();

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

  1. .apply()
  2. .bind()
  3. .call()

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

> function whoami(x, y) {
…   console.log(this, x, y);
… }
> let jdoe = {name: 'John Doe'};
> whoami.apply(jdoe, [1, 2]);
{name: "John Doe"} 1 2
> whoami.call(jdoe, 1, 2);
{name: "John Doe"} 1 2

Из трех вариантов .bind() является наиболее эффективным, поскольку позволяет постоянно изменять значение this для будущих вызовов. Он работает немного по-другому, поскольку возвращает новую функцию, привязанную к данному контексту:

> const newFunction = whoami.bind(jdoe);
> newFunction(1, 2);
{name: "John Doe"} 1 2
> newFunction(3, 4);
{name: "John Doe"} 3 4

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

const collection = {
  items: ['apple', 'banana', 'orange'],
  type: 'fruit',
  show: function() {
    this.items.forEach(callback.bind(this));
  }
};

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

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

> const collection = {
…   items: ['apple', 'banana', 'orange'],
…   type: 'fruit',
…   show: () => {
…     this.items.forEach((item) => {
…       console.log(`${item} is a ${this.type}`);
…     });
…   }
… };
> collection.show();
TypeError: Cannot read property 'forEach' of undefined

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

const collection = {
  items: ['apple', 'banana', 'orange'],
  type: 'fruit',
  show: function() {
    this.items.forEach((item) => {
      console.log(`${item} is a ${this.type}`);
    });
  }
};
collection.show();

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

Этот раздел был лишь верхушкой айсберга. Чтобы узнать больше о необычном поведении JavaScript, ознакомьтесь с примерами сложного кода, которые также доступны в виде устанавливаемого модуля Node.js. Другим ценным источником мудрости является книга Дугласа Крокфорда JavaScript: Хорошие моменты,, в которой также есть раздел, посвященный плохим и ужасным моментам.

Что дальше?

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

Объектная модель документа (DOM)

Если вы планируете заниматься какой-либо разработкой на стороне клиента, то вам не избежать знакомства с DOM.

Примечание: Возможно, вы ранее использовали тот же интерфейс DOM для обработки XML-документов на Python.

Чтобы обеспечить возможность манипулирования HTML-документами на JavaScript, веб-браузеры предоставляют стандартный интерфейс, называемый DOM, который включает в себя различные объекты и методы. Когда страница загружается, ваш скрипт может получить доступ к внутреннему представлению документа через предопределенный экземпляр document:

const body = document.body;

Это глобальная переменная, доступная вам в любом месте вашего кода.

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

  • Вверх: .parentElement
  • Левый: .previousElementSibling
  • Верно: .nextElementSibling
  • Вниз: .children, .firstElementChild, .lastElementChild

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

const html = document.firstElementChild;
const body = html.lastElementChild;
const element = body.children[2].nextElementSibling;

Большинство атрибутов будут иметь значение null, если они не ведут к элементу в дереве. Единственным исключением является свойство .children, которое всегда возвращает объект, подобный массиву, который может быть пустым.

Часто вы не знаете, где находится элемент. Объект document, как и любой другой элемент в дереве, имеет несколько методов для поиска элемента . Вы можете искать элементы по названию тега, атрибуту ID, названию класса CSS или даже с помощью сложного CSS-селектора.

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

  1. .querySelector(selector)
  2. .querySelectorAll(selector)

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

const div = document.querySelector('div'); // The 1st div in the whole document
div.querySelectorAll('p'); // All paragraphs inside that div

Как только у вас есть ссылка на HTML-элемент, вы можете выполнять с ним множество действий, таких как:

  • Прикрепите к нему данные
  • Измените его стиль
  • Измените его содержимое
  • Измените его расположение
  • Сделайте его интерактивным
  • Удалите его совсем

Вы также можете создавать новые элементы и добавлять их в дерево DOM:

const parent = document.querySelector('.content');
const child = document.createElement('div');
parent.appendChild(child);

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

Фреймворки JavaScript

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

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

Чтобы справиться с этими проблемами, люди начали использовать библиотеки JavaScript , которые инкапсулировали общие шаблоны и сделали API браузера немного менее раздражающим. Безусловно, самой популярной библиотекой всех времен является jQuery, которая до недавнего времени превосходила по популярности современные интерфейсные фреймворки:

Search Interest in JavaScript Frameworks According to Google Trends Поиск интереса к JavaScript-фреймворкам в соответствии с Google Trends

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

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

У jQuery хорошая документация и минимальный API, поскольку нужно запомнить только одну функцию - универсальную $(). Вы можете использовать эту функцию для создания и поиска элементов, изменения их стиля, обработки событий и многого другого.

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

Так почему же большинство людей все-таки предпочитают использовать front-end фреймворк?

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

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

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

Если вы хотите получить работу в качестве front-end разработчика или даже инженера полного цикла, то вам следует ознакомиться с должностными инструкциями. Скорее всего, они ожидают увидеть кого-то с опытом работы в одной из этих платформ:

На момент написания этой статьи это были, пожалуй, самые популярные JavaScript-фреймворки.

Заключение

В этом руководстве вы узнали о происхождении JavaScript, его альтернативах и о том, куда движется язык. Вы сравнили Python и JavaScript, более подробно рассмотрев их сходства и различия в синтаксисе, средах выполнения, связанных инструментах и жаргоне. Наконец, вы узнали, как избежать фатальных ошибок в JavaScript.

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

  • Сравнение Python и JavaScript
  • Выберите подходящий язык для работы
  • Написать сценарий оболочки на JavaScript
  • Создание динамического контента на веб-странице
  • Воспользуйтесь преимуществами экосистемы JavaScript
  • Избегайте распространенных ошибок в JavaScript

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

Следующие шаги

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

 

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

 

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

 

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

 

Знакомство с JavaScript - это одна из многих ступенек на пути к тому, чтобы стать успешным веб-разработчиком. Помимо изучения JavaScript, вы должны, по крайней мере, знать о HTML, CSS, протоколе HTTP, политике браузера и их различиях, фреймворках JavaScript и всей экосистеме инструментов, связанных с этим.

 

Существует множество отличных ресурсов для изучения JavaScript, включая платные и бесплатные варианты, которые вы можете найти в Интернете. Веб-документы Mozilla Developer Network (MDN) - это одна из наиболее полных и актуальных баз знаний по JavaScript, содержащая учебные пособия и справочные материалы для людей с разным уровнем квалификации.

Вы также найдете бесплатные электронные книги, такие как "Красноречивый JavaScript" Марийн Хавербеке, "Говорящий на JavaScript" доктора Акселя Раушмайера или серия книг Кайла Симпсона "Ты еще не знаешь JS". серия книг Кайла Симпсона "Ты еще не знаешь JS".<<<6>>

>

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

Если у вас есть веб-проект с серверной частью на Python, такой как REST API, реализованный в Flask или Django, то вы также можете создать для него интерфейс JavaScript. Помните, что практика - это ключ к овладению любым языком программирования.

Удачи и удачного кодирования!

<статус завершения article-slug="python-vs-javascript" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/articles/python-vs-javascript/bookmark/" данные-api-статья-статус завершения-url="/api/v1/articles/python-vs-javascript/completion_status/"> <кнопка поделиться bluesky-text="Интересная статья #Python от @realpython.com :" email-body="Проверить эта статья на Python:%0A%0APython против JavaScript для любителей Python" email-subject="Статья на Python для вас" twitter-text="Интересная статья на #Python от @realpython:" url="https://realpython.com/python-vs-javascript /" url-title="Python против JavaScript для питонистов">

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

Back to Top