Программирование на JavaScript. Урок 2: Строки. Объекты. Прототипы

уровень: Начинающий

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

Здравствуйте и добро пожаловать в лекцию номер 2 курса программирования на JavaScript университета Hexlet. В этой лекции мы поговорим о строках и начнем говорить об объектах. Начнем потому что об объектах мы будем говорить на протяжении всего курса. В прошлый раз я сказал, что мы не прошли ещё пару нюансов касательно чисел, и сегодня мы познакомимся еще с парой нюансов, но еще несколько деталей останутся на потом – на тот момент, когда мы на самом деле столкнемся с ними и это будет иметь более значимый практический смысл. Давайте начнем со строк.

Строки

Строки

В JavaScript всего лишь один тип данных, который содержит строки, и он используется для всего, что касается таких вот символьных значений. Задаются они очень просто. Как видите, здесь на первой строке мы задаем переменную “string 1” и “string 2” и записываем туда какой-то текст. Вы можете использовать двойные кавычки, как в первом примере, или одинарные кавычки, как во втором, — это не имеет значения. Единственное — вам нужно начать и закончить строку одинаковой кавычкой. Как видите, вы можете туда вписывать UTF символы. В первую строку мы вписываем китайские иероглифы, во вторую — простую латиницу. С точки зрения языка нет никакой разницы, с ними можно одинаково работать. У строк есть некоторые свойства и операторы. Сегодня мы познакомимся с буквально парой.

Первая и самая частоиспользуемая, наверное — это “получить длину строки”. Нет ничего проще, нужно лишь обратиться к свойству “length” и мы получим число (в данном случае — 8 символов). Естественно, вам необязательно создавать именно такую переменную, чтобы получить какое-то из ее свойств. Вы можете обратиться напрямую к строке. Когда вы пишете что-то в кавычках, то это автоматически строка и с ней можно делать все то же самое, что вы могли бы делать с переменной (почти). Мы чуть позже увидим разницу. То же самое можно делать с одинарными кавычками и т.д. Если вам нужно вписать какой-то юникод-символ с помощью его кода, то нужно использовать комбинацию “\u”. Также к ним можно обращаться, как и к любой другой строке, в частности код “\u1552” будет превращен в символ “ᕒ”. Естественно, это один символ, поэтому длина этой строки будет 1.

Экранирование запрещенных символов

Экранирование запрещенных символов

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

Собственно, “\u” — это другая такая комбинация. Тут это не запрещенный символ. “u”, естественно, вы можете использовать в строке, но со слешем JavaScript воспринимает, что дальше будет идти юникод символа.

Строки vs. Символы

Строки vs. Символы

В JavaScript есть только один тип для обозначения строк, и он же используется, если вам нужно обозначить один символ. Один символ – это ничто иное, как строка с одним символом. Один из методов строк — это “charAt” – символ по какому-то индексу. С помощью него мы можем получить строку, содержащую один символ по какому-то индексу. Естественно, как в любом языке программирования, мы начинаем с 0, поэтому у первой строки символ с индексом 2 будет “с”. Если же мы введем индекс, которого здесь быть не может, то JavaScript вернет нам пустую строку.

Конкатенация

Конкатенация

Конкатенация или слияние нескольких строк. В JavaScript используется +, как и во многих других языках. Все что нужно сделать — это поставить одну строку, поставить “+”, поставить следующую строку, и они соединятся. В итоге вы получите их склеивание. В данном примере мы берем символ или строку, содержащую один символ с индексом 0 – “a”, с индексом 2 “c”, с индексом 4 – “e” и получаем строку “ace”. Вы можете этот делать, естественно, с готовыми строками.

В третьем примере начинается немного непонятная, но, в принципе, логичная вещь. Если вы в такой конкатенации используете число со строкой (здесь 12 — это число, но 20 — это строка, потому что 20 – в кавычках, а 12 – нет), то все вместе это интерпретируется как строка. 12 здесь не складывается ни с чем, как число, а интерпретируется как строка. С точки зрения JavaScript здесь могли бы быть кавычки и ничего бы не изменилось.

Строки и числа

Строки и числа

Если мы используем математическую операцию не сложения, а, скажем, деления, то происходит обратная операция. Мы берем “12” в виде строки и делим это на 2. Так как здесь не “+”, а математическая операция деления, то “12” JavaScript пытается превратить в число, и оно это может сделать, потому что из строки “12” может получиться число 12. Мы получаем число 12, деленое на 2, + 1. В итоге мы получим число 7. Если же мы попытаемся применить какую-то математическую операцию, кроме сложения, со строкой, которую никак не перевести в число, например, слово “day”, то мы в итоге получим NaN — Not a Number.

Строки и числа 1

Вы можете специально переводить число в строку – для этого используется оператор toString. Если мы берем число 42 и применяем к нему toString, то получаем строку “42”. У строки можно получить длину, а длина — это число. Мы получим 2, потому что 42 состоит из двух цифр. Дальше это число можно обратно перевести в строку, после чего мы получим строку “2”. Так можно двигаться очень далеко, но суть здесь в том, что мы можем соединять операторы и вызовы. Вроде разобрались.

Строки и числа 2

Продолжаем еще немного со строками. Если вам нужно склеить строку и результат математической операции сложения, то вам нужно использовать скобки. В примере мы говорим: строка “Blink” складывается с числом 182, но, так как “Blink” — это строка, в итоге получается сложение двух строк — простое склеивание.

Если же вы хотите сделать сложение, то так, как показано во втором примере, у вас ничего не получится, так как здесь есть одна строка (хоть числа два, но строка здесь уже есть), в итоге все равно получится склеивание трех строк. Получится вот такая фигня, потому что единичка просто вклеилась.

Если же мы математическую операцию сложения выведем в отдельные скобки, то здесь на самом деле будет математическая операция сложения. В итоге у нас получится: “строка + результат замкнутой математической операции 181+1”. Это будет эквивалентно строке “Blink 182”.

Сравнение строк

Сравнение строк

Строки сравниваются, что очень логично, в алфавитном порядке, поэтому “а” меньше чем “b”, но “c” не меньше, чем “b”, потому что “с” стоит после “b” в алфавите. Это касается не только единичных символов, но и целых строк. Там идет сравнение в алфавитном порядке по каждому из символов. Например, “abcd” не меньше “abcd” потому что, на самом деле, это одна и та же строка – в каждой позиции символ не больше и не меньше соответствующего ему символа в другом примере.

Сравнение строк 1

“abcd” меньше, чем “abdc” потому что первые две позиции не отличается, но на третьей позиции в первой строке находится символ “с”, а символ “с” стоит в алфавите пред “d”. Этого уже достаточно, чтобы сказать, что первая строка находится раньше, чем вторая строка, поэтому здесь нам JavaScript вернет “true”.

Сравнение строк 2

Строки можно сравнивать, являются ли они одинаковыми. В частности, строки в данном примере являются одинаковыми. Кстати, о том, почему здесь 3 знака сравнения, а не 2, как это принято во многих языках, мы поговорим отдельно. Пока запомните, что лучше всего использовать (и безопасней всего) именно 3 знака сравнения. Конкатенацию строк JavaScript интерпретирует как строку, поэтому первая строка последнего примера и 3 строки, содержащие буквы той строки в таком же порядке, с точки зрения JavaScript являются идентичными.

Операторы

  • if
  • for
  • while
  • switch
  • break

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

Объекты

Дело в том, что JavaScript это язык, в котором почти все — это объект. На само деле, объект – все, кроме чисел, строк, переменных “true”, “false” и значений “null” и “undefined”. Все остальное — это объект. Объект — это ничто иное, как некий замкнутый контейнер со свойствами. Это совсем не то, что принято называть объектом в классическом языке программирования. В классическом — том, где у нас есть классы и объекты, как имплементации этих классов, как их реализации в более физическом пространстве. Объекты здесь, скорее, как словари в некоторых других языках программирования, то есть, просто блоки, где есть название свойства и его значение. В С, например, самое близкое, что похоже на JavaScript-объект — это структура. Это просто такой контейнер, где есть какие-то переменные. Функция — это объект и массив — это объект, и сам объект — это объект. В течение этого курса мы будем видеть, насколько эта простая идея дает свободу в выражении и сколько на самом деле силы дает эта простая идея.

Объект

Объект

Вот так выглядит объект. Здесь на первой строке у нас пустой объект. Называется он “obj”, задается он так же, как и любая другая переменная и мы говорим, что вот начинается этот замкнутый контейнер и тут же заканчивается. Внутри пусто, этот пустой объект внутри ничего не содержит. На самом деле, теоретически он может содержать что-то, даже если внутри будет пусто и чуть позже вы увидите, как это. Следующий объект на самом деле содержит какие-то свойства. Он содержит здесь два свойства – “name” (имя), значение имени, а также возраст и его значение. В первом свойстве мы используем в качестве значения строку, во втором свойстве — число. Все это выражение одного объекта, поэтому, как и любая строка в JavaScript, это должно заканчиваться точкой с запятой.

Объект 1

Как обратиться к этим свойствам? — Очень легко. Метод, который, наверное, будет лучше всего и удобнее всего использовать — это обращение через точку.

Объект 2

Мы также можем обратиться с помощью квадратных скобок, как если бы это был массив. Самое интересное, что, согласно спецификации языка, названием свойства (идентификатором свойства) должна быть строка. Здесь мы можем указать строку без кавычек – JavaScript поймет, это будет совершенно нормально и, опять же, ничем не будет отличаться от предыдущего примера, где у нас был “name” в кавычках. Но, так как пустая строка в JavaScript — это тоже строка, мы можем использовать эту пустую строку в качестве идентификатора какого-то свойства объекта. На четвертой строчке нашего примера у нас есть идентификатор-пустая строка и у него значение “WEIRD!”. Мы можем обратиться к нему (к сожалению, через точку мы обратиться к пустой строке не можем, но через квадратные скобки – можем) – мы просто указываем пустую строку и получаем “WEIRD!”. С точки зрения языка никакой ошибки здесь нет, это не то чтобы очень плохо и проблематично. Многие в фреймворке просто запрещают это делать – видимо у них есть свои причины.

Объект 2 Объект 3

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

Вложенность

Объект 3 Вложенность

Объект может в качестве значения свойства содержать строку, число и ничто не мешает ему содержать другой объект. Иными словами, значением свойства объекта может быть что угодно в языке, в том числе объект. В данном примере у нас есть свойство “wife” и значение этого свойства — новый объект со своими свойствами (внутри него, конечно же, может быть ещё один объект). Это очень удобно потому что только с помощью объектов мы можем создавать очень гибкие структуры данных и интерпретировать какие-то реальные данные в языке, не заморачиваясь тем, что для этого нужно создавать какие-то классы, новые деревья и т.д. Мы просто вкладываем объект так, как нам нужно, чтобы интерпретировать нашу задачу.

Вложенность 1

Обращаться к таким вложенным данным, что очень логично, как и к значению объекта самого — через точку. Мы можем обратиться к “person.wife” и получим объект (JavaScript нам скажет, что это — объект). У него можно же обратиться к свойству. Мы обращаемся к “age” — получаем 29. Если же мы обратимся к свойству, которого нет в объекте или в объекте, который внутри объекта, JavaScript ответит “undefined”. С помощью этого можно проверять, есть ли там что-то — если это “undefined”, значит его там нет.

Вложенность 1 Вложенность 2

Если нам нужно изменить какое-то значение в объекте, то всего лишь нужно прировнять его к какому-то значению. Здесь мы создаем “person.name”. У объекта “person” нет “height”. Как видите, есть только “name” и “age”, поэтому если мы попросим “height”, то JavaScript скажет “undefined”. Если же мы изменим значение (person.name = “Peter”, person.height = 178), то теперь мы можем к нему обратиться и получить этот “Peter” вместо “Alex”, потому что тут уже обновленное значение, и получить значение нового свойства – “height”. Иными словами, вы можете обновить значение или добавить значение, если его там нет, с одинаковым синтаксисом.

Прототипы

Прототип

Вот самая главная часть сегодняшней лекции — прототипы. JavaScript основан на прототипах. Несмотря на то, что его выразительная сила позволяет создавать классы, предпочтительно использовать JavaScript в соответствии с идеями, с которыми он был создан. А создан он был с идеей прототипов. Вы видели, как легко создавать объекты в JavaScript и нам не нужны никакие классы, чтобы описывать эти объекты. Мы просто создаем нужный нам объект и изменяем его, как хотим. Если нам нужно чтобы какой-то другой объект наследовал свойство чего-то иного, то нам нужно наследовать от объекта. В Java или С++ мы создаем класс и объект этого класса, а если мы хотим, чтобы что-то другое, другая сущность наследовалась от этого, то мы создаем класс, который наследуется от того класса. Здесь мы создаем объект, который наследует от объекта.

Прототип 1

Здесь у нас есть объект “Human”, и у него три свойства – “type, head, legs”. Мы создаем новый объект, который называется “Megahuman” и говорим, что прототипом этого объекта является объект “Human”. В итоге мы получаем такую ситуацию, что мы можем получить значение свойств прототипа.

Прототип 2

Мы можем обратиться к “Megahuman.type”, несмотря на то что в самом “Megahuman” мы не говорили “type” (не устанавливали это свойство), мы получаем это значение от прототипа. Мы также можем обратиться к “head” и можем изменить значение для объекта “Megahuman. head” — сделаем 2. Значение станет 2, но это не изменит значение прототипа. Прототип остается неизменным. Мы можем создать новое свойство в нашем объекте – мы скажем “Megahuman.hands” и дадим ему значение 10. Естественно, если мы его получим, то получим 10. Это свойство не появится у прототипа.

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

Прототип 3

Самое главное в этих прототипах – если в прототипе мы обновим свойство или добавим новое, то оно автоматически, моментально станет доступно во всех объектах, прототипом которого является наш объект. Давайте проверим. В “Human” нет свойства “face”. Здесь есть “type, legs, head” тут нет “face”, поэтому он нам ответит “undefined”. То же самое ответит “Megahuman”. В нем нету “face” и у прототипа нет “face”, поэтому он ответит “undefined”.

Теперь у “Human.face” мы задаем значение “okay”. Это создает новое свойство со значением “okay” и оно автоматически появляется в Megahuman. Теперь мы можем сказать “Megahuman.face” и получим “okay” от прототипа. Если же мы теперь изменим значение в “Megahuman” (здесь мы сделаем “face = awesome”), то в “Megahuman” оно изменится, но в прототипе значение останется тем, которое было – “okay”.

Цепочки

Цепочка

Как это работает. Мы обращаемся к свойству, и если оно есть в объекте, в самом объекте, то оно нам возвращается и все. Если же его в объекте нет, то JavaScript смотрит в прототип. Если в прототипе его нет, то JavaScript смотрит в прототип и так оно делает до тех пор, пока не дойдет до того объекта, в котором это свойство есть. Если оно есть. Эти цепочки не бесконечны, но в данном примере нам повезло — мы дошли до прототипа, в котором это свойство есть – “1”. Поэтому, если у этого объекта мы спросим “head”, то мы получим значение “1”. И получим его от пра-пра-прототипа.

Цепочка 1

Если же среди цепочки нет объекта с таким свойством, как в этом примере (Мы говорим “nose”, его нет ни в одном из прототипов, включая последний. Последний прототип — это системный объект “Object.prototype” – такой же объект, просто идет в комплекте с JavaScript), то мы получим “undefined”. Естественно, мы получим “undefined” на любом из этих уровней, потому что всегда логика одна и та же — мы проверяем текущий объект, у которого мы спрашиваем, если в нем нет этого свойства, то идем к прототипу и, если этого нет нигде, то мы получаем “undefined”.

Цепочка 2

Давайте взглянем снова на наш пример. Помните, мы сделали “Human.face”? Такого нету свойства, потому в среднем звене цепочки (Human) его нет, а для этого “Human” мы не указывали никакого прототипа и его прототипом является “Object prototype”. В нем тоже “face” нет, ну и в “Megahuman”, естественно, тоже.

Цепочка 3

Теперь мы сделали “Human.face = okay”, и, естественно, если мы спросим у “Human.face” значение, то получим “okay”, но, так как прототипом “Magahuman” является “Human”, то, спросив “Megahuman.face”, мы получим “okay”. Так произойдет потому что у “Megahuman.face” значения нет, соответственно, мы идем к прототипу — в нем значение есть. Оно и вернется нам.

Цепочка 4

Теперь мы сделали “Megahuman.face = awesome” и, если мы спрашиваем “Megahuman.face”, то получим значение “awesome” без необходимости идти к прототипу, поскольку у нас в “Megahuman” есть значение. Его мы и получаем. У “Human.face” ничего не изменилось. Теперь нам не нужно думать о том, что есть какие-то правила, просто представляйте эту цепочку — мы идем к прототипам, прототипам, прототипам… и ищем значение везде. Как только находим – возвращаем, если не находим, то “undefined”.

Как указать прототип

Как указать прототип

Все это ясно, но как же указать этот прототип? В текущей версии JavaScript делается это следующим образом – мы делаем наш объект “Megahuman”, но создаем его следующим образом: мы говорим object (это системный объект), создай мне объект с прототипом “Human” (мы указываем объект, который хотим использовать, как прототип). Теперь “Human” — это прототип “Megahuman” и все, о чем мы говорили до этого, будет работать.

Мы будем говорить об объектах и о том, как они на самом деле работают (механизм работы и создания прототипов), и тогда мы коснемся другого способа наследования и того способа, который использовался до того, как в JavaScript появился способ “Object.create”. Сейчас давайте не париться, поскольку делать это очень просто, используя такой синтаксис – правильный и рабочий синтаксис.

Удаление свойства

Удаление свойства

Последнее, о чем стоит сказать — это удаление свойств. Мы говорили о добавлении свойств или изменении существующих, но если нам нужно удалить свойство из объекта, то можно использовать “delete”.

Мы сделали то же самое – “Megahuman” унаследовали от “Human”, “Megahuman.head” сделали значение “2”, несмотря на то, что у “Human.head” значение “1”. Теперь, если мы спрашиваем у “Megahuman”, то “head” будет “2”. Если же мы удалим у этого объекта свойство “head”, то у него все равно будет возвращаться “head”, но уже от прототипа, потому что в нем этого значения нет. Мы вернемся к прототипу — в нем это значение “1”, поэтому после удаления мы получим “1”. Если же мы сделаем “delete Human.head” – удалим значение в прототипе, то мы будем получать здесь не “1”, а “undefined”.

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

Расскажите друзьям:Share on FacebookShare on VKShare on Google+Tweet about this on Twitter