Alfa Brain

Как создать объект в JS? Для этого нужно изучить Java


Превью статьи Как создать объект в JS? Для этого нужно изучить Java

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

При прочтении этой статьи обязательно практикуйтесь, иначе материал усвоится плохо.

Итак, чтобы разобраться в создании объектов в JavaScript нужно изучить синтаксис создания объектов в Java? Так! Стоп! Java? Да, да! Все в порядке - это будет увлекательное путешествие, так что не будем терять время.

Создание объектов (Java)

Для того чтобы потренироваться в Java достаточно онлайн компилятора по типу этого

Как вы возможно знаете, класс (class) представляет схему объектов. Вы создаете объект из класса. Каждый из следующих операторов, взятых из программы CreateObjectDemo (не беспокойтесь, мы разберемся со всем ниже), создает объект и присваивает его переменной:

Point originOne = new Point(23, 94); Rectangle rectOne = new Rectangle(originOne, 100, 200); Rectangle rectTwo = new Rectangle(50, 100);

Первая строка создает объект класса Point, а вторая и третья строки создают объект класса Rectangle.

Каждое из этих утверждений состоит из трех частей (подробно обсуждаемых ниже):

  1. Объявление (Declaration): код, Point originOne, Rectangle rectOne и Rectangle rectTwo, — это все объявления переменных, которые связывают имя переменной с типом объекта. Типом объекта является сам класс.
  2. Создание экземпляра (Instantiation): ключевое слово new — это оператор Java, создающий объект.
  3. Инициализация (Initialization): за оператором new следует вызов конструктора, который инициализирует новый объект.

Объявление переменной для ссылки на объект (Java)

Ранее вы узнали, что для объявления переменной нужно написать:

type name; // псевдокод

Эта запись сообщает компилятору о том, что вы будете использовать name в качестве ссылки на данные, тип которых является типом который вы укажите. В случае с примитивной переменной это объявление также резервирует надлежащий объем памяти для переменной.

Вы также можете объявить ссылочную переменную в отдельной строке. Например:

Point originOne;

Если вы объявите originOne таким образом, его значение будет неопределенным до тех пор, пока объект не будет фактически создан и назначен этой переменной. Простое объявление ссылочной переменной не создает объект. Для этого вам нужно использовать оператор new, как будет описано в следующем разделе. Вы должны присвоить объект originOne, прежде чем использовать его в своем коде. В противном случае вы получите ошибку компилятора.

Переменная в этом состоянии, которая в настоящее время не ссылается ни на один объект, может быть проиллюстрирована следующим образом (имя переменной originOne плюс ссылка, ни на что не указывающая):

img

Создание экземпляра класса (Java)

Оператор new создает экземпляр класса, выделяя память для нового объекта и возвращая ссылку на эту память. Оператор new также вызывает конструктор объекта.

Примечание. Фраза "создание экземпляра класса" означает то же самое, что и "создание объекта". Когда вы создаете объект, вы создаете «экземпляр» класса, поэтому это одно и то же

Оператор new требует один аргумент: вызов конструктора. Имя конструктора предоставляет имя класса.

Оператор new возвращает ссылку на созданный им объект. Эта ссылка обычно присваивается переменной соответствующего типа, например:

Point originOne = new Point(23, 94);

Ссылку, возвращаемую оператором new, не обязательно присваивать переменной. Её также можно использовать непосредственно в выражении. Например:

int height = new Rectangle().height;

Этот код будет обсуждаться в следующем разделе, а пока повторим:

  1. Класс - это шаблон для создания объектов
  2. Класс имеет Конструктор
  3. Конструктор имеет такое же имя как и сам класс
  4. Конструктор это специальная функция в классе в которой описано как создавать объект
  5. Если вызвать оператор new и передать аргументом имя класса, на самом деле вызывается конструктор класса и создается новый объект
  6. Оператор new возвращает ссылку на вновь созданный объект
  7. Ссылку на новый объект можно присвоить переменной или использовать на месте.

Инициализация объекта (Java)

Вот код класса Point:

public class Point { public int x = 0; public int y = 0; // constructor public Point(int a, int b) { x = a; y = b; } }

Этот класс содержит единственный конструктор. Вы можете распознать конструктор, потому что его объявление использует то же имя, что и класс, и не имеет возвращаемого типа. Конструктор в классе Point принимает два целочисленных аргумента, как объявлено в коде (int a, int b). Следующий оператор предоставляет значения 23 и 94 для этих аргументов:

Point originOne = new Point(23, 94);

Результат выполнения этого оператора можно проиллюстрировать на следующем рисунке:

img

Вот код класса Rectangle, который содержит четыре конструктора:

public class Rectangle { public int width = 0; public int height = 0; public Point origin; // четыре конструктора public Rectangle() { origin = new Point(0, 0); } public Rectangle(Point p) { origin = p; } public Rectangle(int w, int h) { origin = new Point(0, 0); width = w; height = h; } public Rectangle(Point p, int w, int h) { origin = p; width = w; height = h; } // метод для передвижения прямоугольника public void move(int x, int y) { origin.x = x; origin.y = y; } // метод для вычисления площади прямоугольника public int getArea() { return width * height; } }

Каждый конструктор позволяет указать начальные значения исходной точки, ширины и высоты прямоугольника, используя как примитивные, так и ссылочные типы. Если класс имеет несколько конструкторов, они должны иметь разные сигнатуры. Компилятор Java различает конструкторы по количеству и типу аргументов.

Когда компилятор Java встречает следующий код, он знает, что нужно вызвать конструктор в классе Rectangle, который требует аргумента Point, за которым следуют два целочисленных аргумента т.е. четвертый конструктор:

Rectangle rectOne = new Rectangle(originOne, 100, 200);

Четвертый конструктор Rectangle, который инициализирует переменную origin со значением originOne. Кроме того, конструктор устанавливает ширину в 100 и высоту в 200. Теперь есть две ссылки на один и тот же объект Point — объект может иметь несколько ссылок на него, как показано на следующем рисунке:

img

Следующая строка кода вызывает конструктор Rectangle (3-й конструктор), которому требуются два целочисленных аргумента, предоставляющих начальные значения ширины и высоты. Если вы проверите код внутри конструктора, вы увидите, что он создает новый объект Point, значения x и y которого инициализируются равными 0:

Rectangle rectTwo = new Rectangle(50, 100);

Конструктор Rectangle, используемый в следующем операторе, не принимает никаких аргументов, поэтому он называется конструктором без аргументов:

Rectangle rect = new Rectangle();

Все классы имеют по крайней мере один конструктор. Если в классе ничего не объявлено явно, компилятор Java автоматически предоставляет конструктор без аргументов, называемый конструктором по умолчанию. Этот конструктор по умолчанию вызывает конструктор без аргументов родительского класса или конструктор Object, если у класса нет другого родителя. Если у родителя нет конструктора (у объекта он есть), компилятор отклонит программу.

Практика (Java)

Теперь немного практики, конечно если вы никогда не писали на Java. Откройте 'этот онлайн компилятор' и скопируйте в него нижележащий код. Поэксперементируйте, попробуйде создать новые экземпляры объектов и вывести в терминал их значения.

class Point { public int x = 0; public int y = 0; // constructor public Point(int a, int b) { x = a; y = b; } } class Rectangle { public int width = 0; public int height = 0; public Point origin; // четыре конструктора public Rectangle() { origin = new Point(0, 0); } public Rectangle(Point p) { origin = p; } public Rectangle(int w, int h) { origin = new Point(0, 0); width = w; height = h; } public Rectangle(Point p, int w, int h) { origin = p; width = w; height = h; } // метод для передвижения прямоугольника public void move(int x, int y) { origin.x = x; origin.y = y; } // метод для вычисления площади прямоугольника public int getArea() { return width * height; } } public class Main { // Тут запускается программа public static void main(String[] args) { Point originOne = new Point(23, 94); System.out.println("originOne.x: " + originOne.x); System.out.println("originOne.y: " + originOne.y); Rectangle rectOne = new Rectangle(originOne, 100, 200); Rectangle rectTwo = new Rectangle(50, 100); Rectangle rectThree = new Rectangle(); } }

Сравнение синтаксиса в JS (JavaScript)

Давайте вспомним синтаксис класса в JavaScript:

class Human() { constructor(name, age) { this.name = name; this.age = age; } }

Мы имеем класс Animal с одним конструктором. При вызове класса с оператором new, отработает конструктор и в результате вызова мы получим ссылку на новый объект.

const person_1 = new Human('John', 25);

В JavaScript у класса может быть только один конструктор. К сожаления в JS нет понятия перегрузки функций (это когда у функции разное поведение в зависимости от количества аргументов) отсюда и только один конструктор.

Когда вызывается конструктор для создания нового объекта, существует переменная this которая и ссылается на пока новый пустой объект в который мы тут же записываем нужные значения. По завершении работы конструктор возвращает ссылку на этот новый объект которую мы сразу сохраняем в переменной person_1.

В JavaScript вызов класса который не имеет конструктора через оператор new все равно вернет пустой объект - это очень похоже на Java.

В целом синтаксис объявления объектов очень похож. Как в Java так и в JavaScript мы имеем классы, конструкторы которые создают новые объекты через оператор new.

Но классы в JavaScript появились только в стандарте ECMAScript 2015. Как же до этого мы создавали объекты.

Ну, новички скажут что просто так:

const person_1 = { name: 'John'; age: 25; };

Верно, этот способ объявления объектов называется литеральным. Мы просто открываем фигурные скобки и вуаля! Объект готов!

Если же нам нужно создавать однотипные объекты, с одними и теми же аргументами мы просто можем создать подобную функцию:

function createHuman(name, age) { return { name: name, age: age, }; } const person_1 = createHuman('John', 25);

Да, это тоже работает, но объекты создаются все еще с помощью литералов. В данном случае у таких объектов свойство [[Prototype]] будет всегда указывать на Object.prototype

person_1.__proto__ === Object.prototype

В этой статье мы не будем рассматривать цепочки прототипов в JS, сейчас нам важно понять как работает конструктор объектов в JavaScript.

Так как же нам создать объект правильно? Со своим собственным прототипом? Все просто, нужно вызвать функцию с ключевым словом new. В таком случае поведение функции в корне отличается от обычного вызова функции. При вызове функции с оператором new функция имеет переменную this которая ссылается на новый пустой объект. В этот пустой объект мы можем записать нужные нам значения. Возвращать this (новый созданный объект) из такой функции совсем не обязательно, JavaScript сделает это за вас.

function Human(name, age) { this.name = name; this.age = age; } const person_1 = new Human('John', 25);

Таким образом вызов функции Human с оператором new превращает ее в конструктор похожий на тот что мы выдели в классе JS выше.

Вызов такой функции без оператора new будет обычным вызовом функции и в нашем случае функция вернет undefined, а this будет глобальным объектом.

Получается функция в JS может быть одновременно и обычной функцией и Классом с конструктором? Верно, такова особенность языка. Это может вводить в заблуждение особенно если вы пришли из более классических языков программирования (например Java). Помните вот что: Язык JavaScript создавался в очень короткие сроки, а обратная совместимость языка тянется в браузерах от самого его зарождения и я считаю, что мы можем закрыть глаза на многие странности языка.

Заключение

Мы разобрали синтаксис создания объектов в Java и JavaScript, сравнили их и попрактиковались. Думаю теперь вам стало понятнее откуда растут ноги спецификации ECMAScript 2015 Classes, что же такое конструктор в JS и их типы (Классы и Функции).

Когда я только начинал изучать JavaScript, мне было сложно понять что функция может создавать объекты при этом являться обычной функцией. Сам концепт конструктора казался очень странным ведь вокруг были одни литералами и это было вполне удобно, пока я не добрался до более сложных задач.

Теперь зная синтаксис создания объектов в Java и JS можно представить на что ориентировался Брендан Эйх (Создатель JS) и откуда черпал концепции.

Надеюсь, я помог вам разобраться в этой нелегкой теме.

thanks

Поделиться: