- Namespace и js-модули
- Способы объявления переменных
- Ключевое слово
void
- Ключевое слово
new
- Основные типы данных
- Проверка типа объекта
- Логические значения и проверка на null
- Установка значений по-умолчанию
- Ключевое слово
this
- Деструктуризация
- Заключение
Мой более-менее серьезный путь в программировании начался с написания программ на языке C#, иногда я пробовал писать на JavaScript, и то и дело впадал в ступор в таких ситуациях, когда неверно указывал имя переменной и узнавал об этом спустя много много лет час отладки, так как со мной рядом не было моего компилятора, который бы меня выручил в трудную минуту. Через некоторое время, помимо C# я начал писать много кода на JavaScript и в настоящее время могу делать это без особых трудностей, меня больше не смущает неявное приведение типов и динамическая типизация.
В данной статье я бы хотел систематизировать свои базовые знания об этих языках и рассмотреть их сходства и различия. Данная статья может служить руководством для C# разработчиков, которые хотят изучить JavaScript и наоборот. Также хочу заметить, что в данной статье описываются возможности клиентского JS, так как опыта разработки на Node.js у меня нет. Итак, если вы все ещё не потеряли интерес - приступим.
Namespace и js-модули
В каждой программе для избежания конфликтов в именах переменных, функций, классов или других объектов мы объединяем их в некоторые области. Таким образом, если две разные области будут содержать элементы с одинаковыми именами, конфликта не произойдет.
В C# для разбиения программы на части используются пространства имен. Для их объявления используется ключевое слово namespace
. Например, если мы хотим создать набор компонентов пользовательского интерфейса, то логично поместить их все в одно пространство имен, например, Components
. При этом принято чтобы пространство имен имело следующее именование [AssemblyName].[DirectoryName].[DirectoryName].[...]
. В каждом файле класс компонента пользовательского интерфейса необходимо поместить внутрь пространства имен:
Содержимое файла ComboBox.cs
:
Для того, чтобы начать использовать компоненты необходимо импортировать их из пространства имён следующим образом using AssemblyName.Components
. При данном способе подключения одной строкой мы импортируем все объекты в текущий файл.
В JS для этих же целей применяются ES-модули. При их использовании мы в какой-то степени эмулируем поведение пространств имен написанием дополнительного кода. Рассмотрим тот же пример с библиотекой компонентов. Допустим у нас есть папка Components
, которая содержит компоненты пользовательского интерфейса ComboBox.js
, Checkbox.js
, Button.js
и тд. Для того чтобы получить схожее поведение по сравнению с пространством имен в папке Components
необходимо создать файл index.js
, который будет содержать следующий код:
Для того, чтобы использовать данные компоненты необходимо импортировать их в текущий файл. Это можно сделать следующим образом: import * as Components from './../Components'
, после ключевого слова from
нам необходимо указать путь к папке, в которой находятся все описанные компоненты.
Способы объявления переменных
Ключевое слово var
Как известно C# является строго типизированным языком программирования, поэтому при объявлении переменной компилятору должен быть известен её тип, для этого обычно он указывается перед её именем.
Но мы также можем сказать компилятору, что он должен вывести тип самостоятельно из выражения, стоящего после знака присваивания. Это стало возможно благодаря введению в версии C# 3.0 ключевого слова var
.
С помощью var
мы можем создавать объекты анонимного типа:
В JavaScript для объявления переменных также можно использовать ключевое слово var
, однако, в отличии от C# областью видимости данных переменных будет вся функция или объект window
, если переменная была объявлена вне функции.
Хоть у вас и есть возможность объявлять переменные с помощью var
в JavaScript, но сейчас этого делать не рекомендуется, после выхода стандарта ES6 было добавлено ключевое слово let
, которое также позволяет объявлять переменные, но его преимуществом заключается в том, что их областью видимости будет являться блок, в котором они объявлены, а не вся функция.
Константы
Как в C#, так и в JavaScript для объявления константного поля используется ключевое слово const
. Правда стоит отметить, что понятие константы в данном случае является различным для данных языков.
В C# константой называется выражение, которое может быть полностью вычислено на этапе компиляции, т.е. константы могут быть числами, логическими значениями, строками или нулевыми ссылками..
В JavaScript значение константы также нельзя изменять, однако нет ограничений, накладываемых на значение как в языке C#, ей можно присваивать любые значения/объекты/массивы. Однако, если в константу присвоен объект, то от изменения защищена сама константа, но не свойства внутри неё:
Ключевое слово void
Во время написания данной статьи, я экспериментировал в консоли с функциями и по првычке начал описывать функцию как в C# void SomeFunction...
, и для меня было большой неожиданностью, когда я узнал, что в JavaScript есть ключевое слово void
. Как оказалось void
в JavaScript является унарным оператором, который вычисляет значение операнда, затем отбрасывает его и возвращает undefined
.
Таким образом, можно сказать, что использование void
явно указывает отсутствие возвращаемого значения, подробнее с примерами его использования вы можете ознакомиться в следующей статье статье.
В C# void
не является оператором, однако по сути имеет схожее значение. Здесь он обозначает отсутствие возвращаемого значения функции:
Однако, как можно заметить в примере выше, void
стоит в том месте, где обычно указывается тип возвращаемого значения, и это не случайно, ведь в C# void
также является типом.
void
в качестве типа может использоваться только в небезопасном контексте при работе с указателями.
Ключевое слово new
В JavaScript ключевое слово new
является оператором, и используется привычным для многих C-подобных языков образом - для создания объекта.
В C# new
может использоваться для следующих целей:
- для создания объектов;
- для скрытия наследуемого члена базового класса;
- чтобы ограничить типы, которые могут использоваться в качестве аргументов для параметра типа в универсальном классе.
Первый случай аналогичен применению new
в JavaScript.
Основные типы данных
В каждом языке имеются типы данных - примитивы, на основе которых строятся другие типы данных, давайте рассмотрим типы данных предоставляемые нам в C# и JavaScript.
Примитивные типы С#:
- Целочисленный со знаком:
sbyte
,short
,int
,long
- Целочисленный без знака:
byte
,ushort
,uint
,ulong
- Символы Unicode:
char
- Набор символов Unicode:
char
- Числа с плавающей запятой:
float
,double
- Десятичный с повышенной точностью:
decimal
- Логическое значение:
bool
Базовый классом является Object
.
Для JS:
Примитивные типы данных:
- Число
number
- Строка
string
- Логический тип
boolean
- Специальное значение
null
- Специальное значение
undefined
symbol
Базовым типом является Object
.
После изучения примитивов обоих языков можно придти к следующим выводам:
- Вместо достаточно большого набора числовых типов в JavaScript имеется единственный тип
number
; - В JavaScript отсутствует тип
char
, вместо него стоит использовать типstring
; - В обоих языках базовым типом является
Object
; - Отличительной особенностью JS является то, что
null
иundefined
выделены в отдельные типы, в то время как в C#null
- это ключевое слово обозначающее отсутствие значения. - В JS присутствует тип
symbol
, который используется в основном внутри самого стандарта JavaScript, для того чтобы иметь возможность добавлять новый функционал без конфликта с существующей кодовой базой.
Как правило, сейчас все больше приложений, в которых необходимо выполнять обработку данных на клиенте, для чего требуется большая точность в вычислениях. В настоящее время в JavaScript отсутствует встроенная возможность работы с большими числами, однако в недалеком будущем планируется добавить новый тип BigInt
. Для решения аналогичных задач в C# имеется класс System.Numerics.BigInteger
.
Проверка типа объекта
Проверка типа является достаточной типичной операцией для большинства языков программирования. Основываясь на типе, мы можем выполнять различные действия. Например, рассмотрим пример из жизни: вы слышите звонок в дверь, если к вам пришел пьяный сосед (объект с типом Пьяный Сосед) чтобы занять денег, то вы вряд ли откроете ему дверь, но если за дверью ваш лучший друг (объект с типом лучший друг), то вы не задумываясь впустите его в квартиру. C# и JavaScript также предоставляют средства для проверки типа объектов.
Оператор typeof
Для получения информации о типе как в C#, так и в JavaScript имеется оператор typeof
. Давайте рассмотрим принцип его работы в обоих языках:
В С# оператор typeof
применяется к типу и возвращает объект класса Type
, который содержит всю информацию о типе.
В JS typeof
возвращает строку, указывающую тип операнда.
В примере выше можно заметить некоторые особенности работы данного оператора. Кажется логичным, если выражение typeof new Animal()
возвращало бы строку 'Animal'
, a typeof [1,2,3]
- строку Array
, однако как бы ни было парадоксально, результатом в обоих случаях является 'object'
. Также в связи с тем, что классы в JS являются оберткой над функциями, то выражение typeof class C {}
вернет 'function'
вместо 'class'
. Ещё одним интересным фактом является то, что выражение typeof null
вернёт 'object'
. В JavaScript данный оператор имеет большой недостаток: все не примитивные объекты для него на одно лицо, все они имеют один тип object
.
Стоит заметить, что в JavaScript typeof
можно применять к чему угодно: объектам, функциям, классам и т.д.. В C# данный оператор применяется лишь к типам.
is и instanceof
Помимо получения информации о типе, порой бывает полезно проверить принадлежность объекта к определенному типу.
В C# для данных целей имеется оператора is
.
В JavaScript для того, чтобы выяснить к какому типу принадлежит объект необходимо использовать оператор - instanceof
.
Логические значения и проверка на null
Практически повсеместно, для того чтобы не получить Null reference exception
, перед использованием переменной мы проверяем её на null
, а в случае с JavaScript, ещё и на undefined
.
В C# мы постоянно видим подобный код:
В JavaScript данную конструкцию можно записать несколько короче. Связано это с тем, что в отличии от C#, в JavaScript множество значений кроме false
при приведении типов также расцениваются как false
:
null
undefined
- «» (пустая строка)
0
NaN
(not a number)
Таким образом, приведенный выше код на C# можно записать следующим образом:
или
В связи с тем, что проверки на null
происходят повсеместно, в C# 6.0 был добавлен Null Propagation Operator .?
.
Код на языке C#:
С его помощью данный участок кода можно переписать следующим образом:
В JavaScript обычно делают следующим образом:
Установка значений по-умолчанию
Ещё одной частой операцией является установка значений по-умолчанию, с версии 2.0 в C# появился Null Coalescing Operator - ??
.
Следующие две строки кода на C# являются эквивалентными:
В JavaScript подобную операцию обычно делают следующим образом.
Однако мы можем применять операторы &&
и ||
только в том случае, если 0
, false
и пустая строка не являются допустимыми значениями.
В обозримом будущем операторы ?.
, ??
должны появиться и в JavaScript (в настоящее время они прошли стадию Stage0), подробнее об этих операторах в JavaScript можно прочитать в статье.
Ключевое слово this
Как в C#, так и в JavaScript имеется ключевое слово this
. Обычно в C# понимание предназначения this
не вызывает никакого труда, однако в JavaScript это является одной из самых сложных концепций языка. Далее рассмотрим применение this
на примерах.
В C# ключевое слово this
указывает на текущий экземпляр класса.
В данном примере в выражении Console.WriteLine(this.name)
, this
указывает на переменную employee
.
Так как this
- текущий экземпляр класса, то его нельзя использовать в методах не привязанных к определенному типу, например в статических методах.
В JavaScript значение this
называется контекстом вызова и будет определено в момент вызова функции. Если одну и ту же функцию запускать в контексте разных объектов, она будет получать разный this
:
К тому же, в JavaScript присутствует возможность явного указания значения this
с помощью функций: call
, bind
, apply
. Например, вышеприведенный пример можно переписать следующим образом:
Деструктуризация
Часто бывает необходимо присвоить несколько полей объекта локальным переменным. Например, как часто вы наблюдаете подобный код?
Для подобных целей можно использовать деструктуризацию. Данную возможность в разной степени поддерживают оба языка.
В C# 7.0 для поддержки деструктуризации появился новый вид функций, называемый деконструкторами. Для того чтобы объявить деконструктор нам необходимо определить метод с именем Deconstruct
, все параметры которого должны быть объявлены с модификатором out
:
Поддержка деструктуризации или (destructuring assignment) в JavaScript появилась в шестом стандарте EcmaScript. С её помощь. можно присвоить массив или объект сразу нескольким переменным, разбив его на части.
Стоит отметить, что деструктуризация в JavaScript имеет больше возможностей, чем в C#:
- Изменение порядка переменных;
- Отсутствие необходимости явного объявления деконструкторов;
- Поддержка деструктуризации массивов;
- Установки значений по-умолчанию;
- Присваивание свойств объекта в переменную с другим именем;
- Поддержка вложенной деструктуризации.
Заключение
В данной статье мы обсудили лишь самые основные концепции языков C# и JavaScript. Но остались не затронуты ещё многие аспекты:
- коллекции
- функции
- классы
- многопоточность
Каждая из этих тем является достаточно обширной и будет раскрыта в дальнейшем в отдельной статье.