js лабораторная 6. Методы объекта, "this"
Скачать 2.52 Mb.
|
Методы объекта, "this" 3 Объекты обычно создаются, чтобы представлять сущности реального мира, будь то пользователи, заказы и так далее: И так же, как и в реальном мире, пользователь может совершать действия: выбирать что-то из корзины покупок, авторизовываться, выходить из системы, оплачивать и т.д. Такие действия в JavaScript представлены свойствами-функциями объекта. Примеры методов 4 Для начала научим нашего пользователя user здороваться. Здесь мы использовали Function Expression, чтобы создать функцию для приветствия, и присвоили её свойству user.sayHi нашего объекта. Затем мы вызвали её. Функцию, которая является свойством объекта, называют методом этого объекта. Итак, мы получили метод sayHi объекта user. (пример 1) Конечно, можно заранее объявить функцию и использовать её в качестве метода, следующим образом: (пример 2) 1) 2) Сокращённая запись метода 5 Существует более короткий синтаксис для методов в литерале объекта: Как показано, можно пропустить ключевое слово "function" и просто написать sayHi(). Нужно отметить, что эти две записи не полностью эквивалентны. Есть тонкие различия, связанные с наследованием объектов, но на данном этапе изучения это неважно. this в методах 6 Обычно методу объекта необходим доступ к информации, которая хранится в объекте, чтобы выполнить с ней какие-либо действия (в соответствии с назначением метода). Например, коду внутри user.sayHi() может понадобиться имя пользователя, которое хранится в объекте user. Для доступа к информации внутри объекта метод может использовать ключевое слово this. Значение this – это объект, который использовался для вызова метода. В примере справа во время выполнения кода user.sayHi() значением this будет являться user (ссылка на объект user). this в методах 7 Технически также возможно получить доступ к объекту без ключевого слова this, ссылаясь на него через внешнюю переменную (в которой хранится ссылка на этот объект). Однако такой код будет ненадёжным. Если мы решим скопировать ссылку на объект user в другую переменную, например, admin = user, и перезапишем переменную user чем- то другим, то будет осуществлён доступ к неправильному объекту при вызове метода из admin. this не является фиксированным 8 В JavaScript ключевое слово this ведёт себя иначе, чем в большинстве других языков программирования. Оно может использоваться в любой функции. В примере справа ошибки нет: Например, здесь одна и та же функция назначена двум разным объектам и имеет различное значение this при вызовах. Общее правило: при вызове obj.f() значение this внутри f равно obj. В приведённом примере это user или admin. У стрелочных функций отсутствует this 9 Стрелочные функции особенные: у них нет своего «собственного» this. Если мы используем this внутри стрелочной функции, то его значение берётся из внешней «нормальной» функции. Например, здесь arrow() использует значение this из внешнего метода user.sayHi(): Это является особенностью стрелочных функций. Они полезны, когда нужно получить значение из внешнего контекста. Конструкторы, создание объектов через "new" 10 Обычный синтаксис {…} позволяет создать только один объект. Но часто требуется создать множество однотипных объектов, таких как пользователи, элементы меню и т.д. Это можно сделать при помощи функции-конструктора и оператора "new". Функция-конструктор Функции-конструкторы являются обычными функциями. Но есть 2 особенности: 1) имя функции-конструктора должно начинаться с большой буквы. 2) функция-конструктор должна вызываться при помощи оператора "new". Пример: Преобразование объектов: toString и valueOf 11 Ранее мы рассмотрели преобразование типов для примитивов. Теперь добавим в нашу картину мира объекты. Бывают операции, при которых объект должен быть преобразован в примитив. Например: • Строковое преобразование – если объект выводится через alert(obj). • Численное преобразование – при арифметических операциях, сравнении с примитивом. • Логическое преобразование – при if(obj) и других логических операциях. Логическое преобразование Проще всего – с логическим преобразованием. Любой объект в логическом контексте – true, даже если это пустой массив [] или объект {}. Строковое преобразование 12 Строковое преобразование проще всего увидеть, если вывести объект при помощи alert: Как видно, содержимое объекта не вывелось. Это потому, что стандартным строковым представлением пользовательского объекта является строка "[object Object]". Такой вывод объекта не содержит интересной информации. Поэтому имеет смысл его поменять на что-то более полезное. Если в объекте присутствует метод toString, который возвращает примитив, то он используется для преобразования. Строковое преобразование 13 Результатом toString может быть любой примитив Метод toString не обязан возвращать именно строку. Его результат может быть любого примитивного типа. Например, это может быть число, как в примере ниже: Поэтому мы и называем его здесь «строковое преобразование», а не «преобразование к строке». Все объекты, включая встроенные, имеют свои реализации метода toString, например: Численное преобразование 14 Для численного преобразования объекта используется метод valueOf, а если его нет – то toString: Метод valueOf обязан возвращать примитивное значение, иначе его результат будет проигнорирован. При этом – не обязательно числовое. У большинства встроенных объектов такого valueOf нет, поэтому численное и строковое преобразования для них работают одинаково. Исключением является объект Date, который поддерживает оба типа преобразований: Две стадии преобразования 15 Итак, объект преобразован в примитив при помощи toString или valueOf. Но на этом преобразования не заканчиваются. Вполне возможно, что в процессе вычислений этот примитив будет преобразован во что-то другое. Например, рассмотрим применение к объекту операции ==: Объект obj был сначала преобразован в примитив, с использованием численного преобразования, получилось 1 == true. Далее, т. к. значения всё ещё разных типов, применяются правила преобразования примитивов, результат: true. Две стадии преобразования 16 То же самое происходит при сложении с объектом Или при вычитании объектов: при помощи +: Функция-конструктор 17 Когда функция вызывается как new User(…), происходит следующее: 1. Создаётся новый пустой объект, и он присваивается this. 2. Выполняется код функции. Обычно он модифицирует this, добавляет туда новые свойства. 3. Возвращается значение this. Другими словами, вызов new User(…) делает следующее: То есть результат вызова new User("Вася") – это тот же объект, что и: Теперь, если будет необходимо создать других пользователей, можно применять new User("Маша"), new User("Даша") и т.д. Это и является основной целью конструкторов – удобное повторное создание однотипных объектов. Возврат значения из конструктора return 18 Обычно конструкторы ничего не возвращают явно. Их задача – записать все необходимое в this, который в итоге станет результатом. Но если return всё же есть, то применяется простое правило: 1) при вызове return с объектом, будет возвращён объект, а не this. 2) при вызове return с примитивным значением, примитивное значение будет отброшено. Другими словами, return с объектом возвращает объект, в любом другом случае конструктор вернёт this. Пример: Возврат значения из конструктора return 19 А вот пример с пустым return (или мы могли бы поставить примитив после return, неважно): Обычно у конструкторов отсутствует return. В данном блоке мы упомянули особое поведение с возвращаемыми объектами, чтобы не оставлять пробелов в изучении языка. Создание методов в конструкторе 20 Использование конструкторов для создания объектов даёт большую гибкость. Можно передавать конструктору параметры, определяющие то, как создавать объект и что в него записывать. В this можно добавлять не только свойства, но и методы. В примере new User(name) создаёт объект с данным именем name и методом sayHi: Методы у примитивов 21 JavaScript позволяет работать с примитивными типами данных – строками, числами и т.д., как будто они являются объектами. У них есть и методы. Рассмотрим ключевые различия между примитивами и объектами. Примитив • Это – значение «примитивного» типа. • Есть 7 примитивных типов: string, number, boolean, symbol, null, undefined и bigint. Объект • Может хранить множество значений как свойства. • Объявляется при помощи фигурных скобок {}, например: {name: "Рома", age: 30}. В JavaScript есть и другие виды объектов: например, функции тоже являются объектами. Примитив как объект 22 Парадокс, с которым столкнулся создатель JavaScript: • Есть много всего, что хотелось бы сделать с примитивами, такими как строка или число. Было бы замечательно, если бы мы могли работать с ними через вызовы методов. • Примитивы должны быть лёгкими и быстрыми. Выбранное решение: 1) Примитивы остаются примитивами. Одно значение, как и хотелось. 2) Язык позволяет осуществлять доступ к методам и свойствам строк, чисел, булевых значений и символов. 3) Чтобы это работало, создаётся специальный «объект-обёртка», который предоставляет нужную функциональность, а после удаляется. Каждый примитив имеет свой собственный «объект-обёртку», которые называются: String, Number, Boolean и Symbol. Таким образом, они имеют разный набор методов. Примитив как объект 23 К примеру, существует метод str.toUpperCase(), который возвращает строку в верхнем регистре. Как он работает: Что на самом деле происходит в str.toUpperCase(): • Строка str – примитив. В момент обращения к его свойству, создаётся специальный объект, который знает значение строки и имеет такие полезные методы, как toUpperCase(). • Этот метод запускается и возвращает новую строку (показывается в alert). • Специальный объект удаляется, оставляя только примитив str. Получается, что примитивы могут предоставлять методы, и в то же время оставаться «лёгкими». Число имеет собственный набор методов. Например, toFixed(n) округляет число до n знаков после запятой. Конструкторы String/Number/Boolean 24 Некоторые языки (например, Java) позволяют явное создание «объектов-обёрток» для примитивов при помощи синтаксиса new Number(1) или new Boolean(false). В JavaScript, это тоже возможно по историческим причинам, но очень не рекомендуется. Возможны катастрофическими последствия. Например: Объекты в if всегда дают true, так что в нижеприведённом примере будет показан alert: null/undefined не имеют методов 25 Особенные примитивы null и undefined являются исключениями. У них нет соответствующих «объектов-обёрток», и они не имеют никаких методов. В некотором смысле, они «самые примитивные». Попытка доступа к свойствам такого значения возвратит ошибку: Способы записи числа 26 Допустим, надо записать число 1 миллиард. Самый очевидный путь: В JavaScript можно использовать букву "e", чтобы укоротить запись числа. Она добавляется к числу и заменяет указанное количество нулей: Другими словами, "e" производит операцию умножения числа на 1 с указанным количеством нулей. Запишем что-нибудь очень маленькое. К примеру, 1 мкс: Запишем мкс в через "e": Отрицательное число после "e" подразумевает деление на 1 с указанным количеством нулей: Шестнадцатеричные, двоичные и восьмеричные числа 27 Шестнадцатеричные числа широко используются в JavaScript для представления цветов, кодировки символов и многого другого. Естественно, есть короткий стиль записи: 0x, после которого указывается число. Например: Не так часто используются двоичные и восьмеричные числа, но они также поддерживаются: 0b для двоичных и 0o для восьмеричных: Есть только 3 системы счисления с такой поддержкой. Для других систем счисления рекомендуется использовать функцию parseInt (рассмотрим позже). toString(base) 28 Метод num.toString(base) возвращает строковое представление числа num в системе счисления base. Например: base может варьироваться от 2 до 36 (по умолчанию 10). Часто используемые: base=16 — для шестнадцатеричного представления цвета, кодировки символов и т.д., цифры могут быть 0..9 или A..F. base=2 — обычно используется для отладки побитовых операций, цифры 0 или 1. base=36 — максимальное основание, цифры могут быть 0..9 или A..Z. Используется весь латинский алфавит для представления числа. Две точки в 123456..toString(36) – это не опечатка. Синтаксис JavaScript предполагает, что после первой точки начинается десятичная часть. А если поставить две точки, то JavaScript понимает, что десятичная часть отсутствует, и начинается метод. Альтернатива: (123456).toString(36). Округление 29 Одна из часто используемых операций при работе с числами – это округление. • Math.floor – Округление в меньшую сторону: 3.1 становится 3, а –1.1 – –2. • Math.ceil – Округление в большую сторону: 3.1 становится 4, а –1.1 – –1. • Math.round – Округление до ближайшего целого: 3.1 становится 3, 3.6 – 4, а –1.1 – –1 • Math.trunc (не поддерживается в IE) – Производит удаление дробной части без округления: 3.1 становится 3, а –1.1 – –1. Ниже представлена таблица с различиями между функциями округления: Округление 30 Функции на предыдущем слайде охватывают все возможные способы обработки десятичной части. Но что если нужно округлить число до n-ого количества цифр в дробной части? Есть 2 решения: 1) Умножить и разделить Например, чтобы округлить число до второго знака после запятой, мы можем умножить число на 100, вызвать функцию округления и разделить обратно. 2) Метод toFixed(n) округляет число до n знаков после запятой и возвращает строковое представление результата. Округляет значение до ближайшего числа, как в большую, так и в меньшую сторону, аналогично методу Math.round: Результатом toFixed является строка. Если десятичная часть короче, чем необходима, будут добавлены нули в конец строки: Неточные вычисления 31 Внутри JavaScript число представлено в виде 64-битного формата. Для хранения числа используется 64 бита: 52 из них используется для хранения цифр, 11 из них для хранения положения десятичной точки (если число целое, то хранится 0), и один бит отведён на хранение знака. Если число слишком большое, оно переполнит 64-битное хранилище, JavaScript вернёт бесконечность: Часто встречающаяся ошибка при работе с числами в JavaScript – это потеря точности. Рассмотрим это (неверное) сравнение: Чему же равна сумма, если не 0.3? Что такое 0.1? Это единица делённая на десять – 1/10, одна десятая. В десятичной системе счисления такие числа легко представимы, по сравнению с 1/3, которая становится бесконечной дробью 0.33333(3). (см. след. слайд) Неточные вычисления 32 Деление на 10 хорошо работает в десятичной системе, но деление на 3 – нет. По той же причине и в двоичной системе счисления, деление на 2 обязательно сработает, а 1/10 становится бесконечной дробью. В JavaScript нет возможности для хранения точных значений 0.1 или 0.2, используя двоичную систему, точно так же, как нет возможности хранить одну третью в десятичной системе счисления. Отметим, что ошибка в точности вычислений для чисел с плавающей точкой сохраняется в любом другом языке, где используется формат IEEE 754, включая PHP, Java, C, Perl, Ruby. Наиболее надёжный способ обойти проблему – это округлить результат, используя метод toFixed(n): |