Лекция
Привет, сегодня поговорим про javascript, обещаю рассказать все что знаю. Для того чтобы лучше понимать что такое javascript , настоятельно рекомендую прочитать все из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend).
Давайте посмотрим, что такого особенного в JavaScript, почему именно он, и какие еще технологии существуют, кроме JavaScript.
JavaScript изначально создавался для того, чтобы сделать web-странички «живыми».
Программы на этом языке называются скриптами. Они подключаются напрямую к HTML и, как только загружается страничка — тут же выполняются.
Программы на JavaScript — обычный текст. Они не требуют какой-то специальной подготовки.
В этом плане JavaScript сильно отличается от другого языка, который называется Java.
Почему JavaScript?
Когда создавался язык JavaScript, у него изначально было другое название: «LiveScript». Но тогда был очень популярен язык Java, и маркетологи решили, что схожее название сделает новый язык более популярным.
Планировалось, что JavaScript будет эдаким «младшим братом» Java. Однако, история распорядилась по-своему, JavaScript сильно вырос, и сейчас это совершенно независимый язык, со своей спецификацией, которая называется ECMAScript, и к Java не имеет никакого отношения.
У него много особенностей, которые усложняют освоение, но по ходу учебника мы с ними разберемся.
Чтобы читать и выполнять текст на JavaScript, нужна специальная программа — интерпретатор. Процесс выполнения скрипта называют «интерпретацией».
Компиляция и интерпретация, для программистов
Строго говоря, для выполнения программ существуют « компиляторы » и « интерпретаторы ».
Компиляторы преобразуют программу в машинный код. Этот машинный код затем распространяется и запускается.
А интерпретаторы, в частности, встроенный JS-интерпретатор браузера — получают программу в виде исходного кода. При этом распространяется именно сам исходный код (скрипт).
Современные интерпретаторы перед выполнением преобразуют JavaScript в машинный код или близко к нему, а уже затем выполняют.
Во все основные браузеры встроен интерпретатор JavaScript, именно поэтому они могут выполнять скрипты на странице.
Но, разумеется, этим возможности JavaScript не ограничены. Это полноценный язык, программы на котором можно запускать и на сервере, и даже в стиральной машинке, если в ней установлен соответствующий интерпретатор.
Современный JavaScript — это «безопасный» язык программирования общего назначения. Он не предоставляет низкоуровневых средств работы с памятью, процессором, так как изначально был ориентирован на браузеры, в которых это не требуется.
В браузере JavaScript умеет делать все, что относится к манипуляции со страницей, взаимодействию с посетителем и, в какой-то мере, с сервером:
JavaScript — быстрый и мощный язык, но браузер накладывает на его исполнение некоторые ограничения.
Это сделано для безопасности пользователей, чтобы злоумышленник не мог с помощью JavaScript получить личные данные или как-то навредить компьютеру пользователя.
Этих ограничений нет там, где JavaScript используется вне браузера, например на сервере . Об этом говорит сайт https://intellect.icu . Кроме того, различные браузеры предоставляют свои механизмы по установке плагинов и расширений, которые обладают расширенными возможностями, но требуют специальных действий по установке от пользователя
Большинство возможностей JavaScript в браузере ограничено текущим окном и страницей.
Современные браузеры могут работать с файлами, но эта возможность ограничена специально выделенной директорией — «песочницей». Возможности по доступу к устройствам также прорабатываются в современных стандартах и частично доступны в некоторых браузерах.
Есть способы это обойти, и они раскрыты в учебнике, но они требуют внедрения специального кода на оба документа, которые находятся в разных вкладках или окнах. Без него, из соображений безопасности, залезть из одной вкладки в другую при помощи JavaScript нельзя.
Есть как минимум три замечательных особенности JavaScript :
Полная интеграция с HTML/CSS.
Простые вещи делаются просто.
Поддерживается всеми распространенными браузерами и включен по умолчанию.
Этих трех вещей одновременно нет больше ни в одной браузерной технологии. Поэтому JavaScript и является самым распространенным средством создания браузерных интерфейсов.
Перед тем, как вы планируете изучить новую технологию, полезно ознакомиться с ее развитием и перспективами. Здесь в JavaScript все более чем хорошо.
HTML 5 — эволюция стандарта HTML, добавляющая новые теги и, что более важно, ряд новых возможностей браузерам.
Вот несколько примеров:
Многие возможности HTML5 все еще в разработке, но браузеры постепенно начинают их поддерживать.
Тенденция: JavaScript становится все более и более мощным и возможности браузера растут в сторону десктопных приложений.
Сам язык JavaScript улучшается. Современный стандарт EcmaScript 5 включает в себя новые возможности для разработки.
Современные браузеры улучшают свои движки, чтобы увеличить скорость исполнения JavaScript, исправляют баги и стараются следовать стандартам.
Тенденция: JavaScript становится все быстрее и стабильнее.
Очень важно то, что новые стандарты HTML5 и ECMAScript сохраняют максимальную совместимость с предыдущими версиями. Это позволяет избежать неприятностей с уже существующими приложениями.
Впрочем, небольшая проблема с HTML5 все же есть. Иногда браузеры стараются включить новые возможности, которые еще не полностью описаны в стандарте, но настолько интересны, что разработчики просто не могут ждать.
…Однако, со временем стандарт меняется и браузерам приходится подстраиваться к нему, что может привести к ошибкам в уже написанном (старом) коде. Поэтому следует дважды подумать перед тем, как применять на практике такие «супер-новые» решения.
При этом все браузеры сходятся к стандарту, и различий между ними уже гораздо меньше, чем всего лишь несколько лет назад.
Тенденция: все идет к полной совместимости со стандартом.
Зачастую, недостатки подходов и технологий — это обратная сторона их полезности. Стоит ли упрекать молоток в том, что он — тяжелый? Да, неудобно, зато гвозди забиваются лучше.
В JavaScript, однако, есть вполне объективные недоработки, связанные с тем, что язык, по выражению его автора (Brendan Eich) делался «за 10 бессонных дней и ночей». Поэтому некоторые моменты продуманы плохо, есть и откровенные ошибки (которые признает тот же Brendan).
Конкретные примеры мы увидим в дальнейшем, т.к. их удобнее обсуждать в процессе освоения языка.
Пока что нам важно знать, что некоторые «странности» языка не являются чем-то очень умным, а просто не были достаточно хорошо продуманы в свое время. В этом учебнике мы будем обращать особое внимание на основные недоработки и «грабли». Ничего критичного в них нет, если знаешь — не наступишь.
В новых версиях JavaScript (ECMAScript) эти недостатки постепенно убирают. Процесс внедрения небыстрый, в первую очередь из-за старых версий IE, но они постепенно вымирают. Современный IE в этом отношении несравнимо лучше.
Создадим программу (скрипт) на JavaScript. Для этого откроем любой текстовый редактор и наберем:
Файл сохраним с расширением html (например hello.html) и затем откроем в браузере. Там появится строка:
Приветствие Hello world.
Глобальный (видимый отовсюду) объект document является текущим html-документом, а его метод (функция) write выводит строку в том месте документа, где ее вызвали. При этом write может содержать произвольное число аргументов (через запятую), которые выводятся последовательно. Например, можно было написать: document.write("Приветствие ", st).
JavaScript имеет C-подобный синтаксис. Например, операторы управления логикой программы (алгоритма) имеют следующий вид:
Каждая команда JavaScript оканчивается точкой с запятой (ее можно не ставить, если команд в строке больше нет). В фигурные скобки окружают блоки из нескольких команд (выше, например, цикл while). Об этом говорит сайт https://intellect.icu . Код скрипта находится между парой тегов <script> ... </script>. Их в документе может быть любое количество и они, обычно, чередуются с обычным текстом и тегами html-разметки. Далее в примерах тег script будет опускаться.
В общем случае, html-файл, в котором бракзер исполняет код JavaScript, состоит из двух разделов: заголовка (head) и тела документа (body):
Скрипт можно также загрузить из внешнего файла, как это сделано выше для файла hello.js (таких подключений "внешних модулей" может быть любое количество).
Непосредственный вывод текста при помощи функции document.write(...) стоит использовать для простых случаев. В реальных проектах принято разделять визуальную составляющую информации (где, что должно находиться на странице), ее внешний вид (он задается стилями) и динамическое поведение (собственно скрипт). JavaScript может обратиться к любому элементу страницы и поменять его. Соответствующий пример мы рассмотрим в конце этого документа.
Базовыми типами в JavaScript являются: числа (Number), строки (String), массивы (Array) и объекты (Object). JavaScript - это полиморфный язык, т.е. тип переменной может меняться и зависит от контекста ее использования. Для объявления переменной служит директива var:
JavaScript Числа
Числа в JavaScript вещественные, но с ними можно делать и целочисленные операции (например, логический сдвиг на 8 бинарных разрядов 1 << 8 равен 2 в степени 8 или 256 ). Существует объект Math, который предоставляет различные методы работы с числами. Например, Math.sqrt(2) - квадратный корень, Math.sin(Math.PI) - синус и число пи, Math.random() - псевдослучайное число от 0 до 1, а Math.floor(x) - целая часть вещественного числа x.
JavaScript Строки
Строки могут быть в двойных ("строка") или одинарных ('строка') кавычках. Их можно чередовать, например "это 'строка' в строке". Знак плюс для строк означает их соединение, причем "строка"+5 равно "строка5", а "строка"+(5+7) - равно "строка12" (сначала складываются числа, результат переводится в строку и присоединяется к строке). Каждая строка имеет свойство length, хранящее ее длину ("123abc".length равно 6). Строки можно сравнивать. Если s1="abc", а s2="acb", то s1 < s2 будет истиной (true), а s1 == s2 - ложью (false).
JavaScript Массивы
Массив можно объявить пустым: friends = [] или инициализировать значениями. В примере выше, массив friends содержит три элемента и его длина (свойство friends.length) равна 3. Можно также задать размер массива, но не определять его элементы: friends = new Array(3). Доступ к элементам массива получается при помощи квадратных скобок, с нумерацией, ведущейся от нуля: frends[0] - это "Jerry", а frends[2] - 137 (элементы массива могут иметь различный тип).
JavaScript Объекты
Объекты похожи на массивы, но доступ к хранимым в них данным происходит не по номеру, а по имени (которое называется свойством). На имя ссылаются, как и в массивах, при помощи квадратных скобок: house["rooms"] или через точку: house.rooms (без кавычек). В обоих случаях получится 5. Новые разновидности данных (свойства) в объект можно добавлять и после его объявления. Например, в дальнейшем, написав house.open = true;, мы добавим в объект house третье свойство "open" со значением true.
JavaScript === vs. ==
Полиморфность языка приводит к некоторым особенностям сравнения величин. В JavaScript существует два вида равенства: двойное == и тройное ===. Операция с двумя равенствами (==) делает сначала преобразование типов, а лишь затем величины сравнивает между собой. Так, "1"==1 равно true (истина), а "1"===1 равно false (ложь, так как величины имеют различный тип: строка и число). К чему будут преобразованы типы зависит от контекста. Например, при сравнении строки и числа, число преобразуется к строке, как и при "складывании" строк ("строка"+5 это "строка5"). Если тип величин известен, лучше использовать более быстрое тройное равенство (нет приведения типов).
Строки в JavaScript менять нельзя. Как только строка создана – она такая навсегда. Любые операции с ними приводят к созданию новых строк. Об этом стоит помнить при анализе производительности кода. Чтобы изменить некоторые символы в строке, можно превратить ее в массив или воспользоваться регулярными выражениями.
Приведем примеры работы со строками (нумерация символов строк, как и в массивах, начинается с нуля):
Существуют функции, понимающие регулярные выражения . Например, заменить в строке все (параметр g) слова "тяжело" на слово "легко", без учета высоты букв (параметр i), можно следующим образом:
Выражение в косых чертах (слешах) называется шаблоном поиска. Он может содержать различные специальные символы:
^ - начало строки, [xy] - один из символов ([abcd] - то же, что [a-d]), . - любой символ, + - повторение предыдущего символа 1 или более раз, {n,m} повторение в интервале [n...m] раз, \s - пробел, табуляция или перевод каретки, \d - цифра [0-9] и т.д. Приведем примеры шаблонов (опуская слеши):
Ключевое слово function объявляет функцию. Например, факториал числа (n!=n·(n-1)·...·1 и 0!=1!=1) дает следующая функция:
Если написать: document.write("5!=", fac(5)), то в документе появится: 5!=120.
JavaScript Цикл while
Первая строка с условием if - "защита" от отрицательных чисел, а ключевое слово return возвращает значение функции. Далее начинает работать цикл while, который "крутится" до тех пор, пока выражение в круглых скобках истинно. В этих скобках происходит уменьшение числа n на 1. Возможны два способа уменьшения: n-- и --n (оба эквивалентны n = n-1). Однако, в первом случае сначала проводится проверка условия, а затем уменьшение, а во втором (-- впереди) - сначала число уменьшается, а только затем участвует в проверке. В JavaScript, как и в C/C++, число "считается истинным", если оно отлично от нуля и ложным при нулевом значении. Поэтому while крутится до тех пор, пока n больше единицы. При n=1 получаем цепочку:
while(--1) ⇒ while( 0 ) ⇒ while(false) ⇒ цикл не выполняем.
В JavaScript функция одновременно является именем класса (потенциального множества однотипных объектов). Следующий код декларирует существование класса House, который хранит в себе 3 свойства (addr, rooms, open), а также может выполнять определенные действия (имеет методы):
Описав класс, можно создать произвольное число объектов - экземпляров этого класса. Для этого служит оператор new:
В результате работы этого скрипта получится:
Обратим внимание, что когда мы описываем класс как абстрактную сущность, доступ к свойствам осуществляется при помощи указателя this. А для экземпляра класса доступ к свойствам делается как и для любого объекта (выше так выведены значения свойства house.open и villa.open двух различных объектов).
Все методы которые доступны объектам (экземплярам класса) должны объявляться при помощи свойства prototype. Если его нет, то метод является статическим. Он не доступен экземплярам, но может быть вызван самим классом (выше - функция вычисления площади окружности). Подобным статическим образом организованы константы (свойства) и методы встроенного в JavaScript объекта Math (выше - число PI).
Но в современном JavaScript есть и более продвинутая конструкция «class», которая предоставляет новые возможности, полезные для объектно-ориентированного программирования.
Базовый синтаксис выглядит так:
Затем используйте вызов new MyClass() для создания нового объекта со всеми перечисленными методами.
При этом автоматически вызывается метод constructor(), в нем мы можем инициализировать объект.
Например:
Когда вызывается new User("Иван"):
…Затем можно вызывать на объекте методы, такие как user.sayHi().
Методы в классе не разделяются запятой
Частая ошибка начинающих разработчиков – ставить запятую между методами класса, что приводит к синтаксической ошибке.
Синтаксис классов отличается от литералов объектов, не путайте их. Внутри классов запятые не требуются.
Итак, что же такое class? Это не полностью новая языковая сущность, как может показаться на первый взгляд.
Давайте развеем всю магию и посмотрим, что такое класс на самом деле. Это поможет в понимании многих сложных аспектов.
В JavaScript класс – это разновидность функции.
Взгляните:
Вот что на самом деле делает конструкция class User {...}:
При вызове метода объекта new User он будет взят из прототипа, как описано в главе F.prototype. Таким образом, объекты new User имеют доступ к методам класса.
На картинке показан результат объявления class User:
Можно проверить вышесказанное и при помощи кода:
Иногда говорят, что class – это просто «синтаксический сахар» в JavaScript (синтаксис для улучшения читаемости кода, но не делающий ничего принципиально нового), потому что мы можем сделать все то же самое без конструкции class:
Результат этого кода очень похож. Поэтому, действительно, есть причины, по которым class можно считать синтаксическим сахаром для определения конструктора вместе с методами прототипа.
Однако есть важные отличия:
Во-первых, функция, созданная с помощью class, помечена специальным внутренним свойством [[FunctionKind]]:"classConstructor". Поэтому это не совсем то же самое, что создавать ее вручную.
В отличие от обычных функций, конструктор класса не может быть вызван без new:
Кроме того, строковое представление конструктора класса в большинстве движков JavaScript начинается с «class …»
Методы класса являются неперечислимыми. Определение класса устанавливает флаг enumerable вfalse для всех методов в "prototype".
И это хорошо, так как если мы проходимся циклом for..in по объекту, то обычно мы не хотим при этом получать методы класса.
Классы всегда используют use strict. Весь код внутри класса автоматически находится в строгом режиме.
Также в дополнение к основной, описанной выше, функциональности, синтаксис class дает ряд других интересных возможностей, с которыми мы познакомимся чуть позже.
Как и функции, классы можно определять внутри другого выражения, передавать, возвращать, присваивать и т.д.
Пример Class Expression (по аналогии с Function Expression):
Аналогично Named Function Expression, Class Expression может иметь имя.
Если у Class Expression есть имя, то оно видно только внутри класса:
Мы даже можем динамически создавать классы «по запросу»:
Как и в литеральных объектах, в классах можно объявлять вычисляемые свойства, геттеры/сеттеры и т.д.
Вот пример user.name, реализованного с использованием get/set:
При объявлении класса геттеры/сеттеры создаются на User.prototype, вот так:
Пример с вычисляемым свойством в скобках [...]:
Старым браузерам может понадобиться полифил
Свойства классов добавлены в язык недавно.
В приведенном выше примере у класса User были только методы. Давайте добавим свойство:
Свойство name не устанавливается в User.prototype. Вместо этого оно создается оператором new перед запуском конструктора, это именно свойство объекта.
Базовый синтаксис для классов выглядит так:
MyClass технически является функцией (той, которую мы определяем как constructor), в то время как методы, геттеры и сеттеры записываются в MyClass.prototype.
В следующих главах мы узнаем больше о классах, включая наследование и другие возможности.
JavaScript позволяет рисовать на html-странице. Для этого служит тег canvas, в котором задается ширина (width) и высота (height) области рисования. Ниже приведен простой пример html-документа:
В его заголовке объявляется класс стиля с именем dashed. Если канвас (описанный в теле документа) содержит свойство class="dashed", то область рисования окажется обведенной пунктирной рамкой. Тег канвас, по-мимо указания размеров и класса стиля содержит идентификатор id = "canID". По этому идентификатору к канвасу можно "достучаться" из JavaScript:
В этом примере сначала получается объект html-страницы canvas при помощи функции getElementById. Этот объект имеет функцию getContext которая возвращает контекст рисования. Это тоже объект, со множеством функций рисования графических примитивов и работы с растровой графикой. В частности, в последней строке функции выводится залитый прямоугольник размерами 100 x 20 в центре канваса (объект canvas знает свои размеры). Вот к чему это приводит:
Кроме канваса, выводить графику можно при помощи векторных картинок в svg-формате или средствами WebGL, позволяющего работать с 3D-графикой.
JavaScript Память
В JavaScript переменные, объявленные как строки, массивы или объекты, являются на самом деле указателями на память, где хранятся соответствующие данные. Это позволяет без особых затрат возвращать, например, объекты из функции или передавать их в функцию в качестве аргументов.
Разберем подробнее особенности работы с указателями на следующем примере (справа зеленым - результат работы скрипта):
Вначале выделяется память для хранения объекта с одним свойством: { v:1 }. На эту память ссылаются 2 переменные x и y. Поэтому изменение свойства v в одной (любой) переменной, "почувствует" и другая. Затем, в присвоении x = { v:4 }, выделяется память на хранение еще одного объекта. Переменная "x" ссылается на него, а "y" ссылается на старый объект. Теперь изменения свойства v этих переменных происходят независимо (каждое в своей памяти). Наконец, при присвоении y=x, переменная "y" начинает ссылаться (как и "x") на новый объект. На старый объект уже ни кто не ссылается и сборщик мусора браузера эту память освободит. Ситуация со строками аналогична, и переменная объявленная как x="строка", является указателем на память, где строка хранится.
Сборщик мусора, освобождающий неиспользуемую память, в современных браузерах достаточно эффективен. Однако, ему стоит помогать, указывая когда память становится не нужной. Например, пусть для вычислений был выделен очень большой массив var ar = new Array(1000000). После его использования стоит написать ar = null, что приведет к освобождению памяти. Ключевое слово null означает "нулевой адрес памяти" (это указатель, который никуда не ведет).
JavaScript null vs. undefined
Переменные могут быть также неопределенными (вообще ни куда не ссылаться). Между неопределенным свойством undefined и нулевым свойством null есть заметная разница. Пусть obj = { x:null }. Тогда свойство obj.y является undefined (его там нет), а свойство obj.x в объекте есть (и под его указатель выделена память), но оно (свойство) ссылается на "нулевой" адрес памяти. При сравнении без приведения типов null === undefined получится false (это различные сущности), a null == undefined - это true.
В низкоуровневых языках, таких как C/С++ или Assembler, программист должен заботиться, как о выделении памяти, так и о ее освобождении. JavaScript в этом отношении существенно более дружественный язык. Например, если при обращении к элементу массива мы выходим за границы массива (свойство length), ни чего "плохого" не происходит и размер массива автоматически увеличивается. Впрочем, за все необходимо платить. И в данном случае - чуть более низким (хотя и не существенно) быстродействием по сравнению с низкоуровневыми языками.
Раз зашла речь о быстродействии, проведем несколько тестов. Так как они занимают время, будем запускать их по нажатию кнопки run. Для этого добавим в html-файл следующий код:
В результате появится кнопка (type="button") с надпиью "run" (value="run"), при нажатии на которую запустится функция speed (onclick="speed()"). Последняя строка функции поменяет текст в теге <b>: время = ms (нажмите на кнопку несколько раз).
Функция getElementById объекта document находит на html-странице тег со свойством id="timeID" и возвращает его как объект (аналогично канвасу). У него есть свойство innerHTML - текст внутри тега: <b id="timeID">...</b>. В этом месте и выводится время вычислений в миллисекундах (ms) и среднее значение по totRuns запускам. Для этого, перед началом вычислений в переменной time запоминается время, а после вычислений оно вычитается из времени на тот момент. Текущее время возвращает окно браузера - объект window.
Ниже в таблице приведено время в миллисекундах работы рекурсивной функции Fib1(n) в JavaScript и С++ (gcc 64bit). Кроме этого, дано время суммирования в цикле по i c 10000000 итерациями некоторых величин:
Fib(35) | Fib(40) | i | i*i | sqrt(i*0.1) | sin(i*0.1) | |
---|---|---|---|---|---|---|
JavaScript | 100 | 1100 | 140 | 140 | 130 | 330 |
C++ | 60 | 700 | 25 | 30 | 315 | 600 |
Как видно, быстродействие скриптового языка JavaScript вполне сравнимо с быстродействием C++. Заметим, что синус от "целочисленных" аргументов (например, sin(i*1)) почти в 5 раз медленнее, чем от "вещественных" (например, sin(i*0.1)). Небольшого ускорения можно добиться также, положив var sin = Math.sin; и затем писать не Math.sin(...), а sin(...) и т.д.
Google Chrome часто повторяемые участки JavaScript кода компилирует в ассемблер. Поэтому, вычисления с "дорогими" вещественными функциями, подобными exp или sin будут работать со сравнимой скоростью. В целочисленных вычислениях C++, конечно, быстрее, т.к. не производит ненужных преобразований типов переменных.
На комплексных тестах JavaScript, уступает С++, впрочем, не значительно. Например, задача коммивояжера, решаемая методом ветвей и границ на JavaScript требует примерно на 25% больше времени, чем тот же алгоритм, реализованный на C++. В большинстве случаев это не критично.
К сожалению, в одной статье не просто дать все знания про javascript. Но я - старался. Если ты проявишь интерес к раскрытию подробностей,я обязательно напишу продолжение! Надеюсь, что теперь ты понял что такое javascript и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)
Ответы на вопросы для самопроверки пишите в комментариях, мы проверим, или же задавайте свой вопрос по данной теме.
Комментарии
Оставить комментарий
Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)
Термины: Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)