Введение в методы и свойства
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/object.
Все значения в JavaScript, за исключением null и undefined , содержат набор вспомогательных функций и значений, доступных «через точку».
Такие функции называют «методами», а значения – «свойствами». Здесь мы рассмотрим основы использования свойств и методов.
Свойство str.length
У строки есть свойство length , содержащее длину:
alert( "Привет, мир!".length ); // 12
Можно и записать строку в переменную, а потом запросить её свойство:
var str = "Привет, мир!"; alert( str.length ); // 12
Метод str.toUpperCase()
Также у строк есть метод toUpperCase() , который возвращает строку в верхнем регистре:
var hello = "Привет, мир!"; alert( hello.toUpperCase() ); // "ПРИВЕТ, МИР!"
Вызов метода – через круглые скобки!
Обратите внимание, для вызова метода обязательно нужны круглые скобки.
Посмотрите, например, результат обращения к toUpperCase без скобок:
var hello = "Привет"; alert( hello.toUpperCase ); // function.
Метод – это встроенная команда («функция», мы поговорим о них позже), которую нужно вызвать для получения значения. При обращении без скобок мы получим саму эту функцию. Как правило браузер выведет её как-то так: «function toUpperCase() < . >» .
А чтобы получить результат – нужно произвести её вызов, добавив скобки:
var hello = "Привет"; alert( hello.toUpperCase() ); // ПРИВЕТ
Более подробно с различными свойствами и методами строк мы познакомимся в главе Строки.
Метод num.toFixed(n)
Есть методы и у чисел, например num.toFixed(n) . Он округляет число num до n знаков после запятой, при необходимости добивает нулями до данной длины и возвращает в виде строки (удобно для форматированного вывода):
var n = 12.345; alert( n.toFixed(2) ); // "12.35" alert( n.toFixed(0) ); // "12" alert( n.toFixed(5) ); // "12.34500"
Детали работы toFixed разобраны в главе Числа.
Обращение к методам чисел
К методу числа можно обратиться и напрямую:
alert( 12.34.toFixed(1) ); // 12.3
…Но если число целое, то будет проблема:
Методы объектов JavaScript
В определении функции ключевое слово this ссылается на «владельца» функции.
Так, в примере выше this это объект person, который «владеет» функцией fullName.
Другими словами, this.firstName означает свойство firstName данного объекта.
Подробнее о ключевом слове this см. главу Ключевое слово this
Методы JavaScript
Методы JavaScript это действия, которые можно выполнить с объектами.
Метод JavaScript это свойство, содержащее определение функции.
Свойство | Значение |
---|---|
firstName | John |
lastName | Doe |
age | 50 |
eyeColor | blue |
fullName | function() |
Методы это функции, хранящиеся как свойства объекта.
Обращение к методам объекта
Чтобы вызвать метод объекта, используется следующий синтаксис:
Обычно обращение fullName() указывает на метод объекта person, а fullName на его свойство.
Свойство fullName будет выполняться (как функция), если его вызвать с круглыми скобками ().
В следующем примере мы обращаемся к методу fullName() объекта person:
name = person.fullName();
Если вызвать свойство fullName, без круглых скобок (), то будет возвращено определение функции:
name = person.fullName;
Использование встроенных методов
В следующем примере используется метод toUpperCase() объекта String, чтобы преобразовать текст в верхний регистр:
var message = "Hello world!"; var x = message.toUpperCase();
После выполнения кода этого примера, значением переменной x будет строка «HELLO WORLD!».
Добавление методов в объект
Добавить новый метод в объект очень просто:
person.name = function () < return this.firstName + " " + this.lastName; >;
Методы объекта, «this»
Объекты обычно создаются, чтобы представлять сущности реального мира, будь то пользователи, заказы и так далее:
// Объект пользователя let user = < name: "John", age: 30 >;
И так же, как и в реальном мире, пользователь может совершать действия: выбирать что-то из корзины покупок, авторизовываться, выходить из системы, оплачивать и т.п.
Такие действия в JavaScript представлены функциями в свойствах.
Примеры методов
Для начала давайте научим нашего пользователя user здороваться:
let user = < name: "John", age: 30 >; user.sayHi = function() < alert("Привет!"); >; user.sayHi(); // Привет!
Здесь мы просто использовали Function Expression (функциональное выражение), чтобы создать функцию приветствия, и присвоили её свойству user.sayHi нашего объекта.
Затем мы можем вызвать ee как user.sayHi() . Теперь пользователь может говорить!
Функцию, которая является свойством объекта, называют методом этого объекта.
Итак, мы получили метод sayHi объекта user .
Конечно, мы могли бы использовать заранее объявленную функцию в качестве метода, вот так:
let user = < // . >; // сначала, объявляем function sayHi() < alert("Привет!"); >// затем добавляем в качестве метода user.sayHi = sayHi; user.sayHi(); // Привет!
Объектно-ориентированное программирование
Когда мы пишем наш код, используя объекты для представления сущностей реального мира, – это называется объектно-ориентированным программированием или сокращённо: «ООП».
ООП является большой предметной областью и интересной наукой самой по себе. Как выбрать правильные сущности? Как организовать взаимодействие между ними? Это – создание архитектуры, и на эту тему есть отличные книги, такие как «Приёмы объектно-ориентированного проектирования. Паттерны проектирования» авторов Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес или «Объектно-ориентированный анализ и проектирование с примерами приложений» Гради Буча, а также ещё множество других книг.
Сокращённая запись метода
Существует более короткий синтаксис для методов в литерале объекта:
// эти объекты делают одно и то же user = < sayHi: function() < alert("Привет"); >>; // сокращённая запись выглядит лучше, не так ли? user = < sayHi() < // то же самое, что и "sayHi: function()" alert("Привет"); > >;
Как было показано, мы можем пропустить ключевое слово «function» и просто написать sayHi() .
Нужно отметить, что эти две записи не полностью эквивалентны. Есть тонкие различия, связанные с наследованием объектов (что будет рассмотрено позже), но на данном этапе изучения это неважно. Почти во всех случаях сокращённый синтаксис предпочтителен.
Ключевое слово «this» в методах
Как правило, методу объекта обычно требуется доступ к информации, хранящейся в объекте, для выполнения своей работы.
Например, коду внутри user.sayHi() может потребоваться имя пользователя, которое хранится в объекте user .
Для доступа к информации внутри объекта метод может использовать ключевое слово this .
Значение this – это объект «перед точкой», который используется для вызова метода.
let user = < name: "John", age: 30, sayHi() < // "this" - это "текущий объект". alert(this.name); >>; user.sayHi(); // John
Здесь во время выполнения кода user.sayHi() значением this будет являться user (ссылка на объект user ).
Технически также возможно получить доступ к объекту без ключевого слова this , обратившись к нему через внешнюю переменную (в которой хранится ссылка на этот объект):
let user = < name: "John", age: 30, sayHi() < alert(user.name); // "user" вместо "this" >>;
…Но такой код ненадёжен. Если мы решим скопировать ссылку на объект user в другую переменную, например, admin = user , и перезапишем переменную user чем-то другим, тогда будет осуществлён доступ к неправильному объекту при вызове метода из admin .
Это показано ниже:
let user = < name: "John", age: 30, sayHi() < alert( user.name ); // приведёт к ошибке >>; let admin = user; user = null; // перезапишем переменную для наглядности, теперь она не хранит ссылку на объект. admin.sayHi(); // TypeError: Cannot read property 'name' of null
Если бы мы использовали this.name вместо user.name внутри alert , тогда этот код бы сработал.
«this» не является фиксированным
В JavaScript ключевое слово «this» ведёт себя иначе, чем в большинстве других языков программирования. Его можно использовать в любой функции, даже если это не метод объекта.
В следующем примере нет синтаксической ошибки:
function sayHi()
Значение this вычисляется во время выполнения кода, в зависимости от контекста.
Например, здесь одна и та же функция назначена двум разным объектам и имеет различное значение «this» в вызовах:
let user = < name: "John" >; let admin = < name: "Admin" >; function sayHi() < alert( this.name ); >// используем одну и ту же функцию в двух объектах user.f = sayHi; admin.f = sayHi; // эти вызовы имеют разное значение this // "this" внутри функции - это объект "перед точкой" user.f(); // John (this == user) admin.f(); // Admin (this == admin) admin['f'](); // Admin (нет разницы между использованием точки или квадратных скобок для доступа к объекту)
Правило простое: если вызывается obj.f() , то во время вызова f , this – это obj . Так что, в приведённом выше примере это либо user , либо admin .
Вызов без объекта: this == undefined
Мы даже можем вызвать функцию вообще без объекта:
function sayHi() < alert(this); >sayHi(); // undefined
В строгом режиме ( «use strict» ) в таком коде значением this будет являться undefined . Если мы попытаемся получить доступ к this.name – это вызовет ошибку.
В нестрогом режиме значением this в таком случае будет глобальный объект ( window в браузерe, мы вернёмся к этому позже в главе Глобальный объект). Это – исторически сложившееся поведение this , которое исправляется использованием строгого режима ( «use strict» ).
Обычно подобный вызов является ошибкой программирования. Если внутри функции используется this , тогда она ожидает, что будет вызвана в контексте какого-либо объекта.
Последствия свободного this
Если вы до этого изучали другие языки программирования, то вы, вероятно, привыкли к идее «фиксированного this » – когда методы, определённые в объекте, всегда имеют this , ссылающееся на этот объект.
В JavaScript this является «свободным», его значение вычисляется в момент вызова метода и не зависит от того, где этот метод был объявлен, а скорее от того, какой объект вызывает метод (какой объект стоит «перед точкой»).
Эта концепция вычисления this в момент исполнения имеет как свои плюсы, так и минусы. С одной стороны, функция может быть повторно использована в качестве метода у различных объектов (что повышает гибкость). С другой стороны, большая гибкость увеличивает вероятность ошибок.
Здесь наша позиция заключается не в том, чтобы судить, является ли это архитектурное решение в языке хорошим или плохим. Скоро мы поймем, как с этим работать, как получить выгоду и избежать проблем.
У стрелочных функций нет «this»
Стрелочные функции особенные: у них нет своего «собственного» this . Если мы ссылаемся на this внутри такой функции, то оно берётся из внешней «нормальной» функции.
Например, здесь arrow() использует значение this из внешнего метода user.sayHi() :
let user = < firstName: "Ilya", sayHi() < let arrow = () =>alert(this.firstName); arrow(); > >; user.sayHi(); // Ilya
Это особенность стрелочных функций. Она полезна, когда мы на самом деле не хотим иметь отдельное this , а скорее хотим взять его из внешнего контекста. Позже в главе Повторяем стрелочные функции мы увидим больше примеров на эту тему.
Итого
- Функции, которые находятся в свойствах объекта, называются «методами».
- Методы позволяют объектам «действовать»: object.doSomething() .
- Методы могут ссылаться на объект через this .
Значение this определяется во время исполнения кода.
- При объявлении любой функции в ней можно использовать this , но этот this не имеет значения до тех пор, пока функция не будет вызвана.
- Функция может быть скопирована между объектами (из одного объекта в другой).
- Когда функция вызывается синтаксисом «метода» – object.method() , значением this во время вызова является object .
Также ещё раз заметим, что стрелочные функции являются особенными – у них нет this . Когда внутри стрелочной функции обращаются к this , то его значение берётся извне.
Задачи
Использование «this» в литерале объекта
важность: 5
Здесь функция makeUser возвращает объект.
Каким будет результат при обращении к свойству объекта ref ? Почему?
function makeUser() < return < name: "John", ref: this >; > let user = makeUser(); alert( user.ref.name ); // Каким будет результат?
Ответ: ошибка.
function makeUser() < return < name: "John", ref: this >; > let user = makeUser(); alert( user.ref.name ); // Error: Cannot read property 'name' of undefined
Это потому, что правила, которые определяют значение this , никак не смотрят на объявление объекта. Важен лишь момент вызова.
Здесь значение this внутри makeUser() равно undefined , потому что оно вызывается как функция, а не через «точечный» синтаксис как метод.
Значение this одно для всей функции, блоки кода и объектные литералы на него не влияют.
Таким образом, ref: this фактически принимает текущее this функции makeUser() .
Мы можем переписать функцию и вернуть то же самое this со значением undefined :
function makeUser() < return this; // на этот раз нет литерала объекта >alert( makeUser().name ); // Error: Cannot read property 'name' of undefined
Как вы можете видеть, результат alert( makeUser().name ) совпадает с результатом alert( user.ref.name ) из предыдущего примера.
Вот противоположный случай:
function makeUser() < return < name: "John", ref() < return this; >>; > let user = makeUser(); alert( user.ref().name ); // John
Теперь это работает, поскольку user.ref() – это метод. И значением this становится объект перед точкой . .
Определение методов
Начиная с ECMAScript 6, существует короткий синтаксис для определения методов в инициализаторе объекта. По сути, это сокращение для функции, которая назначена имени метода.
Синтаксис
var obj = < property([parameters]) <>, get property() <>, set property(value) <>, * generator() <> >;
Описание
Короткий синтаксис похожий на синтаксис getter’ов и setter’ов представленных в ECMAScript 5.
var obj = foo: function () >, bar: function () >, >;
Вы теперь можете сократить до:
var obj = foo() >, bar() >, >;
Сокращение методов-генераторов
Методы-генераторы также могут быть определены используя короткий синтаксис. Обратите внимание, что звёздочка (*) в коротком синтаксисе должна быть перед именем свойства генератора. То есть, * g()<> будет работать, а g *()<> не будет.
// Используя свойство с именем (pre-ES6) var obj2 = g: function* () var index = 0; while (true) yield index++; >, >; // Тот же объект используя короткий синтаксис var obj2 = *g() var index = 0; while (true) yield index++; >, >; var it = obj2.g(); console.log(it.next().value); // 0 console.log(it.next().value); // 1
Определения методов (ES6) не могут быть конструкторами
Все определения методов кроме методов-генераторов не могут быть конструкторами и будут выбрасывать TypeError если вы попытаетесь создать их экземпляр.
var obj = method() >, >; new obj.method(); // TypeError: obj.method is not a constructor var obj = *g() >, >; new obj.g(); // Генератор
Примеры
Простой тестовый пример
var obj = a: "foo", b() return this.a; >, >; console.log(obj.b()); // "foo"
Вычисляемые имена свойств
Короткий синтаксис также поддерживает вычисляемые имена свойств.
var bar = foo0: function () return 0; >, foo1() return 1; >, ["foo" + 2]() return 2; >, >; console.log(bar.foo0()); // 0 console.log(bar.foo1()); // 1 console.log(bar.foo2()); // 2
Спецификации
Specification |
---|
ECMAScript Language Specification # sec-method-definitions |
Совместимость с браузерами
BCD tables only load in the browser