Что такое конструктор в js. JavaScript. Создание объектов. Добавление метода к объекту
Всем привет. В сегодняшней статье мы разберём, что такое Объекты, конструкторы и прототипы в JavaScript . JavaScript очень отличается от других объектно-ориентированных языков программирования, как, например, C++, Java, PHP и т.д. В этих языках используются классы , на основе которых потом создаются объекты . В JavaScript же классов нет, но есть объекты . Всё кроме null, undefined, string, number, boolean в JavaScript является объектом , поэтому очень важно знать, как они работают. Что же, давайте начнём.
Объект - это набор свойств и методов . Рассмотрим пример:
Var person = {};
var person = new Object();
Здесь мы создали пустой объект. 1 и 2 случай идентичны, но первый короче и поэтому используется чаще. Давайте заполним наш объект какими-то свойствами
Var person = {
name: "Alex",
age: 27
};
Слева указывается название свойства, а через двоеточие его значение. Свойства отделяются друг от друга запятой. Теперь мы можем узнать значение свойства, изменить его или добавить новое.
Console.log(person.name); // выводит в консоль имя
console.log(person["name"]); // аналогично первому случаю
person.age = 30; // изменили свойство age с 27 на 30
person.gender = "male"; // добавили новое свойство gender со значением male
Здесь всё понятно, но поясню второй способ обращения к свойству. Он используется обычно, когда нужно обратиться к свойству, имя которого записано в переменной. Такое бывает, например, в циклах. Там мы не можем обратиться к свойству через точку, будет ошибка, поэтому мы используем квадратные скобки.
Var k = "name";
console.log(person.k); // Ошибка!
console.log(person[k]); // выведет значение
Если мы попытаемся обратиться к несуществующему свойству, то получим undefined.
Console.log(person.height); // Такого свойства нет - undefined
Теперь разберём, что же такое методы.
Методы
- это просто функции в объекте.
Var person = {
name: "Alex",
greet: function() {
console.log("Hello! My name is " + this.name);
}
};
В объекте person мы создали метод greet , который выводит в консоль приветствие. This в коде означает текущий объект, т.е. мы могли бы написать person.name и это также прекрасно работало бы, но что если нам нужно, например, поменять имя объекта? Тогда нам пришлось везде бы менять person на новое имя. С использованием this таких проблем нет.
Что такое объект разобрались. Давайте теперь поговорим, что же такое конструктор
?
Конструктор
- это просто функция, вызывая которую с ключевым словом new
, мы получим новый объект
. Рассмотрим пример:
Var Person = function(name, age) {
this.name = name;
this.age = age;
};
var human = new Person("Alex", 27);
Интепретатор JavaScript видит ключевое слово new и запускает функцию, которая создаёт пустой объект, на который ссылается this . Дальше функция просто присваивает свойствам name и age переданные значения, а затем отдаёт новый объект, на который и передаётся ссылка в переменную human. Да-да! Я не случайно написал ссылка. Дело в том, что все объекты хранятся где-то в памяти, а в переменную попадает лишь ссылка на него, но не он сам.
Var a = 5;
var b = 7;
a = b;
console.log(a, b);
В данном примере мы переменной a присваиваем значение переменной b, поэтому в консоль выведется "7 7". Теперь давайте попробуем сделать так же и с объектами
Var a = {
name: "Alex"
};
Var b = {
name: "Bob"
};
B.name = "John";
console.log(a.name);
Если вы запустите данный пример, то увидите John, т.к. переменная a и b ссылаются на один и тот же объект.
Теперь давайте поговорим о прототипах . Практически любой объект в JavaScript имеет другой, связанный с ним объект, который и называется прототипом . Объект наследует свойства своего прототипа, или проще говоря, все свойства и методы, которые есть у прототипа будут доступны через наш дочерний объект. Создать их очень просто
Var person = {
name: "Alex"
};
var object = Object.create(person);
console.log(object.name);
В данном примере мы создаём новый объект object, который наследует все свойства от его прототипа person с помощью функции create.
Теперь поговорим о том, зачем нам всё это нужно? Допустим, в вашей программе вам нужно создавать много однотипных объектов
Var person = {
name: "Alex",
age: 27,
greet: function() {
}
};
Var person2 = {
name: "John",
age: 17,
greet: function() {
console.log("My name is " + this.name);
}
};
Var person3 = {
name: "Jessica",
age: 37,
greet: function() {
console.log("My name is " + this.name);
}
};
Прописывать все свойства и одинаковые методы в данном случае глупо. Это очень сильно усложнит ваш код и понизит его производительность. Рассмотрим, как сделать правильно
Var Person = {
constructor: function(name, age) {
this.name = name;
this.age = age;
return this;
},
Greet: function() {
console.log("My name is " + this.name);
}
};
Var person, person2, person3;
Person = Object.create(Person).constructor("Alex", 27);
person2 = Object.create(Person).constructor("John", 17);
person3 = Object.create(Person).constructor("Jessica", 37);
Наш код стал короче и более производительным. Давайте теперь разберёмся, что же мы сделали. Для начала мы вынесли общий для всех объектов метод greet в прототип Person. Теперь у нас не 3 функции, а только одна, что хорошо скажется на производительности. Значения name и age у нас для всех объектов разные, поэтому мы создали метод constructor , в котором просто написали функцию, которая инициализирует новый объект и возвращает его. Дальше мы создаём новый объект при помощи Object.create() и сразу вызываем его метод constructor , куда передаём данные. Так мы можем создавать сколько угодно новых объектов, обращаться к их свойствам и вызывать их методы.
Console.log(person.name); // Alex
console.log(person2.age); // 17
console.log(person3.name); // Jessica
Person.greet(); // My name is Alex
person2.greet(); // My name is John
person3.greet(); // My name is Jessica
Класс в языке JavaScript это чисто условное понятие и под классом понимают всего лишь множество всех объектов, которые наследуют свои свойства от одного прототипа. Т.е. в нашем случае эти 3 объекта(person, person2, person3) имеют один класс, потому что они наследуют свойства от одного прототипа.
Для того, чтобы определить является ли объект прототипом другого объекта, есть метод isPrototypeOf
Person.isPrototypeOf(person); // true
Итак, сегодня вы узнали такие основные вещи, как объекты, конструкторы и прототипы . Надеюсь, вы всё поняли, но если всё-таки остались вопросы - оставляйте их в комментариях. До скорого!
Гибкость Javascript позволяет создавать объекты множеством способов. Но как это нередко случается, разнообразие таит в себе множество подводных камней. Из этой статьи Вы узнаете о том, как разглядеть и обогнуть эти опасные рифы.
Основы основ
Нелишним будет напомнить, что из себя представляют объекты в Javascript и как их можно создавать. Объект в Javascript - это всего лишь хэш-таблица ключей и значений. Если значения представляют собой базовые типы или другие объекты, их называют свойствами , если же это функции, их называют методами объекта.Объекты, созданные пользователем, можно изменить в любой точке выполнения скрипта. Многие свойства встроенных в язык объектов также изменяемы. То есть можно просто создать пустой объект и добавлять к нему свойства и методы по мере необходимости. Проще всего это сделать с помощью литеральной нотации
:
//создаем пустой объект
var cat = {};
//добавляем свойство:
cat.name = "Garfield";
//или метод:
cat.getName = function() {
return cat.name;
};
Другим способом создания объекта является использование функций-конструкторов:
// литеральная нотация:
var cat = {name: "Garfield"};
// конструктор-функция:
var cat = new Object();
cat.name = "Garfield";
Очевидно, что литеральная нотация короче конструктора. Есть и философская причина предпочитать литеральную нотацию конструкторам: она подчеркивает, что объект - это всего лишь изменяемый хэш, а не нечто, создаваемое по шаблону, заданному классом.
Кроме того, использование конструктора Object вынуждает интерпретатор проверять, не переопределена ли эта функция в локальном контексте.
Подводный камень конструктора Object
Причин использовать конструктор Object нет. Но все мы знаем, что иногда приходится использовать какой-то старый код, и в этом случае полезно знать об одной особенности этого конструктора. Он принимает аргумент, и в зависимости от его типа может поручить создание объекта другому встроенному в язык конструктору; в итоге мы получим не тот объект, что ожидали://пустой объект var o = new Object(); o.constructor === Object; // true var o = new Object(1); o.constructor === Number; //true var o = new Object("string"); o.constructor === String; //true //мы не предполагали, что созданный объект будет иметь этот метод: typeof o.substring; // "function"
Такое поведение конструктора Object может привести к неожиданным результатам, если мы передаем в него значение, неизвестное на этапе выполнения.
Мораль очевидна: не используйте конструктор Object .
Собственные конструкторы
Мы можем определять собственные конструкторы. Использование их выглядит так:var cat = new Cat("Garfield"); cat.say(); // "I am Garfield"
Синтаксис похож на конструктор Java, но в Javascript конструктор является обычной функцией и поэтому определяется так:
var Cat = function(name) { this.name = name; this.say = function() { return "I am" + this.name; } }
При вызове этого конструктора с оператором new внутри функции происходит следующее:
- создается пустой объект, на который указывает переменная this ; этот объект наследует прототип функции;
- к объекту, хранимому в this , добавляются свойства и методы;
- объект, хранимый в this , неявно возвращается в конце функции (т.к. мы ничего не возвращали явно).
Cat.prototype.say = function() { return "I am " + this.name; };
Кроме того, не совсем корректно утверждать, что объект this , неявно создаваемый в конструкторе, пуст: он наследуется от прототипа Cat , однако рассмотрение прототипов выходит за рамки этой статьи.
Что возвращает конструктор
При использовании оператора new , конструктор всегда возвращает объект. По умолчанию, это объект, на который ссылается this . Конструктор возвращает this неявно, однако мы можем явно вернуть любой другой объект, например:var Cat = function() { this.name = "I am Garfield"; var that = {}; that.name = "I am Cat-woman"; return that; }; var cat = new Cat(); cat.name // "I am Cat-woman"
Таким образом, мы можем вернуть из конструктора любое значение, но лишь при условии, что это объект. Если мы попытаемся вернуть, скажем, строку или false , это не приведет к ошибке, но оператор возврата будет проигнорирован, и конструктор вернет this .
Коварный new
Конструкторы - это всего лишь функции, вызываемые с оператором new . Что случится, если забыть этот оператор? Интерпретатор не выдаст предупреждений, но это приведет к логическим ошибкам. Переменная this будет указывать не на объект, унаследованный от прототипа конструктора, а на глобальный объект (window в случае браузера):function Cat() { this.name = "Garfield"; } // новый объект var cat = new Cat(); typeof cat; // "object" cat.name; // "Garfield" //забываем new: var cat = Cat(); typeof cat; // "undefined" window.name; // "Garfield"
В строгом режиме стандарта ECMAScript 5 this в этом случае не будет указывать на глобальный объект. Посмотрим, как можно избежать этой ошибки, если ECMAScript 5 недоступен.
Соглашения об именовании функций
Самым простым способом является неукоснительное соблюдение соглашений об именовании функций: начинаем обычные функции со строчной буквы (myFunction()), а функции-конструкторы - с заглавной (MyConstruction()). К сожалению, такой способ почти ни от чего не спасает.Явный возврат объекта
Конструкторы могут возвращать любые объекты. Программисты могут воспользоваться этим:function Cat() { var that = {}; that.name = "Garfield"; return that; }
Имя переменной that выбрано произвольно, это не часть спецификации. С тем же успехом мы можем назвать возвращаемый объект me или self или как Вам заблагорассудится.
Для простых объектов, вроде создаваемого в примере, мы можем вообще обойтись без дополнительных переменных, используя литеральную нотацию:
function Cat() {
return {
name: "Garfield"
};
}
Такой конструктор будет всегда возвращать объект, независимо от того, как его вызывать:
var first = new Cat(),
second = Cat();
first.name; // "Garfield"
second.name; // "Garfield"
У этого способа есть серьезный недостаток: объект не наследует прототип конструктора, то есть методы и свойства, добавленные непосредственно к Cat , будут недоступны создаваемым с его помощью объектам.
Самовызывающий конструктор
Для решения этой проблемы достаточно проверить, является ли this в теле конструктора экземляром этого самого конструктора, и если нет, вызывать себя снова, но на этот раз с оператором new . Звучит страшно, но на деле просто:function Cat() { if (!(this instanceof Cat)) { return new Cat(); } this.name = "Garfield"; } Cat.prototype.meow = "Meow!"; var first = new Cat(), second = Cat(); first.name; // "Garfield" second.name; // "Garfield" first.meow; // "Meow!" second.meow; // "Meow!"
Если наш конструктор будет в дальнейшем переименован, придется править его тело. Избежать этого можно проверкой arguments.callee вместо имени конструктора:
if (!(this instanceof arguments.callee)) { return new arguments.callee(); }
Здесь мы воспользовались тем, что внутри каждой функции создается объект arguments , содержащий все параметры, передаваемые функции в момент вызова. Свойство callee этого объекта указывает на вызываемую функцию. Но и здесь нужно проявить осторожность: строгий режим ECMAScript 5 вызывает исключение TypeError при обращении к этому свойству, поэтому стоит заранее сделать выбор между удобством рефакторинга и светлым завтра.
Вместо заключения
Javascript - потрясающий язык. Его достаточно легко освоить, а существующие фреймворки позволят без труда использовать его в своих проектах. Но за простотой синтаксиса скрываются целые рифы подводных камней - и очень мощных инструментов. Иногда полезно смотреть, что же там на дне.The constructor method is a special method for creating and initializing an object created within a class .
The source for this interactive example is stored in a GitHub repository. If you"d like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
Syntax
constructor([arguments ]) { ... }Description
There can be only one special method with the name "constructor" in a class. Having more than one occurrence of a constructor method in a class will throw a SyntaxError error.
Class Square extends Polygon { constructor(length) { // Here, it calls the parent class" constructor with lengths // provided for the Polygon"s width and height super(length, length); // Note: In derived classes, super() must be called before you // can use "this". Leaving this out will cause a reference error. this.name = "Square"; } get area() { return this.height * this.width; } set area(value) { this.area = value; } }
Another example
Take a look at this code snippet
Class Polygon { constructor() { this.name = "Polygon"; } } class Square extends Polygon { constructor() { super(); } } class Rectangle {} Object.setPrototypeOf(Square.prototype, Rectangle.prototype); console.log(Object.getPrototypeOf(Square.prototype) === Polygon.prototype); //false console.log(Object.getPrototypeOf(Square.prototype) === Rectangle.prototype); //true let newInstance = new Square(); console.log(newInstance.name); //Polygon
Here the prototype of Square class is changed but still the constructor of the previous base class Polygon is called when a new instance of a square is being created.
Default constructors
As stated, if you do not specify a constructor method a default constructor is used. For base classes the default constructor is:
Constructor() {}
For derived classes, the default constructor is:
Constructor(...args) { super(...args); }
Specifications
Specification | Status | Comment |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) |
Standard | Initial definition. |
ECMAScript Latest Draft (ECMA-262) The definition of "Constructor Method" in that specification. |
Draft |
Browser compatibility
The compatibility table on this page is generated from structured data. If you"d like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.
Update compatibility data on GitHub
Desktop | Mobile | Server | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Chrome | Edge | Firefox | Internet Explorer | Opera | Safari | Android webview | Chrome for Android | Firefox for Android | Opera for Android | Safari on iOS | Samsung Internet | Node.js | |
constructor | Chrome
Full support
49 Notes Full support 49Notes Notes | Edge Full support 13 | Firefox Full support 45 | IE No support No | Opera Full support 36 | Safari Full support 9 | WebView Android
Full support
49 Notes Full support 49Notes Notes From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript". | Chrome Android Full support Yes | Firefox Android Full support 45 | Opera Android ? | Safari iOS Full support 9 | Samsung Internet Android Full support Yes | nodejs Full support 6.0.0 Full support 6.0.0 Full support 4.0.0 |
Function Person(first, last, age, eye) { this.firstName = first; this.lastName = last; this.age = age; this.eyeColor = eye; }
Считается хорошей практикой программирования, чтобы название функции конструкторов начиналось с большой буквы.
Объектные типы (Шаблоны) (Классы)
Примеры из предыдущих глав значительно ограничены. В них создается только одиночные объекты.
Тем не менее, иногда требуется иметь некий "шаблон", по которому можно было бы создавать множество объектов одного и того же "типа".
Для создания "объектного типа" и используется функция конструктора объекта .
В приведенном в начале этой главы примере функция Person() является функцией конструктора объекта.
Объекты одного и того же типа создаются при помощи вызова функции конструктора с ключевым словом new :
Var myFather = new Person("John", "Doe", 50, "blue"); var myMother = new Person("Sally", "Rally", 48, "green");
Ключевое слово this
В JavaScript ключевое слово this обозначает объект, которому "принадлежит" данный код.
Значением ключевого слова this , когда оно используется в объекте, является сам объект.
В функции конструктора у ключевого слова this нет значения. Это "подстановка" для нового объекта. Когда будет создан новый объект, тогда значением ключевого слова this и станет этот новый объект.
Обратите внимание, что this это не переменная, а ключевое слово. Вы не можете изменять его значение.
Добавление свойства к объекту
Добавить новое свойство к существующему объекту очень просто:
MyFather.nationality = "English";
Свойство будет добавлено к объекту myFather, но не к объекту myMother. (Или какому-либо другому объекту типа person).
Добавление метода к объекту
Добавить новый метод к существующему объекту очень просто:
MyFather.name = function () { return this.firstName + " " + this.lastName; };
Метод будет добавлен к объекту myFather, но не к объекту myMother. (Или какому-либо другому объекту типа person).
Добавление свойства к конструктору объекта
Нельзя добавлять новое свойство к конструктору объекта тем же способом, как это делается в случае с существующим объектом.
Чтобы добавить новое свойство к конструктору, вы должны добавить его в функцию конструктора:
Function Person(first, last, age, eyecolor) { this.firstName = first; this.lastName = last; this.age = age; this.eyeColor = eyecolor; this.nationality = "English"; }
При этом свойствам объекта можно устанавливать значения по умолчанию.
Добавление метода к конструктору объекта
Функция конструктора также может определять методы:
Function Person(first, last, age, eyecolor) { this.firstName = first; this.lastName = last; this.age = age; this.eyeColor = eyecolor; this.name = function() {return this.firstName + " " + this.lastName;}; }
Нельзя добавлять новые методы к конструктору объекта тем же способом, как это делается в случае с существующим объектом. Добавление методов к объекту должно происходить внутри функции конструктора:
Function Person(firstName, lastName, age, eyeColor) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.eyeColor = eyeColor; this.changeName = function (name) { this.lastName = name; }; }
Функция changeName() присваивает значение параметра name свойству lastName объекта person:
MyMother.changeName("Doe");
JavaScript знает, о каком объекте идет речь, "подставляя" в ключевое слово this объект myMother .
Встроенные конструкторы JavaScript
В JavaScript есть встроенные конструкторы для собственных объектов:
Var x1 = new Object(); // Новый объект Object var x2 = new String(); // Новый объект String var x3 = new Number(); // Новый объект Number var x4 = new Boolean(); // Новый объект Boolean var x5 = new Array(); // Новый объект Array var x6 = new RegExp(); // Новый объект RegExp var x7 = new Function(); // Новый объект Function var x8 = new Date(); // Новый объект Date
В этом списке нет объекта Math(), так как это глобальный объект. Ключевое слово new нельзя использовать с объектом Math.
А вы знали?
Как видно из приведенного выше кода, в JavaScript есть объектные версии примитивных типов данных String, Number и Boolean. Однако нет никаких причин создавать для этих типов комплексные объекты. Примитивные значения работают быстрее.
Таким образом:
- используйте объектные литералы { } вместо new Object().
- используйте строковые литералы "" вместо new String().
- используйте числовые литералы 12345 вместо new Number().
- используйте логические литералы true / false вместо new Boolean().
- используйте литералы-массивы вместо new Array().
- используйте литералы-шаблоны /()/ вместо new RexExp().
- используйте выражения функций () {} вместо new Function().
JavaScript предоставляет разработчикам возможность создавать объекты и работать с ними. Для этого существуют следующие приёмы:
- Оператор new
- Литеральная нотация
- Конструкторы объектов
- Ассоциативные массивы
Используем оператор new
Это, наверное, самый легкий способ создания объекта. Вы просто создаете имя объекта и приравниваете его к новому объекту Javascript.
//Создаем наш объект var MyObject = new Object(); //Переменные MyObject.id = 5; //Число MyObject.name = "Sample"; //Строка //Функции MyObject.getName = function() { return this.name; }
Минус данного способа заключается в том, что вы можете работать только с одним вновь созданным объектом.
//Используем наш объект alert(MyObject.getName());
Литеральная нотация
Литеральная нотация является несколько непривычным способом определения новых объектов, но достаточно легким для понимания. Литеральная нотация работает с версии Javascript 1.3.
//Создаем наш объект с использованием литеральной нотации MyObject = { id: 1, name: "Sample", boolval: true, getName: function() { return this.name; } }
Как видите, это довольно просто.
Объект = { идентификатор: значение, ... }
И пример использования:
Alert(MyObject.getName());
Конструкторы объектов
Конструкторы объектов - это мощное средство для создания объектов, которые можно использовать неоднократно. Конструктор объекта - это, по сути, обычная функция Javascript, которой так же можно передавать различные параметры.
Function MyObject(id, name) { }
Только что мы написали конструтор. С помощью него мы и будем создавать наш объект.
Var MyFirstObjectInstance = new MyObject(5,"Sample"); var MySecondObjectInstace = new MyObject(12,"Othe Sample");
Таким образом мы создали различные экземпляры объекта. Теперь мы можем работать отдельно с каждым экземпляром объекта MyObject, не боясь того, что, изменяя свойства одного экземпляра, мы затронем свойства другого экземпляра.
Как и в ООП, у MyObject могут быть методы и различные свойства. Свойствам можно присвоить значения по умолчанию, либо значения, переданные пользователем в конструкторе объекта.
Function MyObject(id, name) { //Значения переданные пользователем this._id = id; this._name = name; //Значение по умолчанию this.defaultvalue = "MyDefaultValue"; }
Аналогичным образом мы можем создавать и функции.
Function MyObject(id,name) { this._id = id; this._name = name; this.defaultvalue = "MyDefaultValue"; //Получение текущего значения this.getDefaultValue = function() { return this.defaultvalue; } //Установка нового значения this.setDefaultValue = function(newvalue) { this.defaultvalue = newvalue; } //Произвольная функция this.sum = function(a, b) { return (a+b); } }
Ассоциативные массивы
Подобный метод будет полезен упорядочивания большого числа однотипных объектов.
Var MyObject = new Number(); MyObject["id"] = 5; MyObject["name"] = "SampleName";
Для обхода таких объектов можно использовать такой цикл:
For (MyElement in MyObject) { //Код обхода //В MyElement - идентификатор записи //В MyObject - содержание записи }
По материалу подготовлена небольшая схема.
Вы можете её посмотреть в форматах.