ООП
в
JavaScript
Один из важнейших принципов ООП – отделение внутреннего интерфейса от внешнего.
- Внутренний интерфейс – это свойства и методы, доступ к которым может быть осуществлен только из других методов объекта, их также называют «приватными» (есть и другие термины, встретим их далее).
- Внешний интерфейс – это свойства и методы, доступные снаружи объекта, их называют «публичными».
Прототип
Объекты в JavaScript можно организовать в цепочки так, чтобы свойство, не найденное в одном объекте, автоматически искалось бы в другом.
Связующим звеном выступает специальное свойство __proto__.
Объект, на который указывает ссылка __proto__, называется «прототипом».
Другими словами, прототип – это «резервное хранилище свойств и методов» объекта, автоматически используемое при поиске.
Обычный цикл for..in не делает различия между свойствами объекта и его прототипа.
Вызов obj.hasOwnProperty(prop) возвращает true, если свойство propпринадлежит самому объекту obj, иначе false.
var animal = {
eats: true
};
var rabbit = {
jumps: true,
__proto__: animal
};
alert( rabbit.hasOwnProperty('jumps') );
// true: jumps принадлежит rabbit
alert( rabbit.hasOwnProperty('eats') );
// false: eats не принадлежитЧто делать, если нужен объект БЕЗ прототипа???
Объект, создаваемый при помощи Object.create(null) не имеет прототипа, а значит в нём нет лишних свойств.
Чтение: Object.getPrototypeOf(obj)
Возвращает obj.__proto__ (кроме IE8-)
Запись: Object.setPrototypeOf(obj, proto)
Устанавливает obj.__proto__ = proto (кроме IE10-).
Создание объекта с прототипом: Object.create(proto, descriptors)
Создаёт пустой объект с __proto__, равным первому аргументу (кроме IE8-), второй необязательный аргумент может содержать дескрипторы свойств.
Prototype
let sleep = {
isLikeSleep: true
};
function Student (name) {
this.name = name;
}
Student.prototype = sleep;
let ivanov = new Student("Ivan");
console.log(ivanov.isLikeSleep);// trueСтранности
let testObj = {};
testObj.toString();//"[object Object]"
let testNumber = 100;
testNumber.valueOf();// 100
testNumber.toFixed(5);// "100.00000"
testNumber.toString();// "100"testString = "bla-bla";
testString.length; // 7
testString.slice(-4); // "-bla"let testBoolean = true;
testBoolean.valueOf(); //true
testBoolean.toString(); //"true"
function Animal(name) {
this.speed = 0;
this.name = name;
this.run = function(speed) {
this.speed += speed;
console.log( this.name + ' бежит, скорость ' + this.speed );
};
this.stop = function() {
this.speed = 0;
console.log( this.name + ' стоит' );
};
};
let testAnimal = new Animal('Зверь');
console.log( animal.speed ); // 0, начальная скорость
animal.run(3); // Зверь бежит, скорость 3
animal.run(10); // Зверь бежит, скорость 13
animal.stop(); // Зверь стоитfunction Animal(name) {
this.name = name;
this.speed = 0;
}
// методы в прототипе
Animal.prototype.run = function(speed) {
this.speed += speed;
console.log( this.name + ' бежит, скорость ' + this.speed );
};
Animal.prototype.stop = function() {
this.speed = 0;
console.log( this.name + ' стоит' );
};
var animal = new Animal('Зверь');
console.log( animal.speed ); // 0, свойство взято из прототипа
animal.run(5); // Зверь бежит, скорость 5
animal.run(5); // Зверь бежит, скорость 10
animal.stop(); // Зверь стоитДостоинства
- Функциональный стиль записывает в каждый объект и свойства и методы, а прототипный – только свойства. Поэтому прототипный стиль – быстрее и экономнее по памяти.
Недостатки
- При создании методов через прототип, мы теряем возможность использовать локальные переменные как приватные свойства, у них больше нет общей области видимости с конструктором.
// 1. Конструктор Animal
function Animal(name) {
this.name = name;
this.speed = 0;
}
// 1.1. Методы -- в прототип
Animal.prototype.stop = function() {
this.speed = 0;
console.log( this.name + ' стоит' );
}
Animal.prototype.run = function(speed) {
this.speed += speed;
console.log( this.name + ' бежит, скорость ' + this.speed );
};
// 2. Конструктор Rabbit
function Rabbit(name) {
this.name = name;
this.speed = 0;
}
// 2.1. Наследование
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;
// 2.2. Методы Rabbit
Rabbit.prototype.jump = function() {
this.speed++;
console.log( this.name + ' прыгает, скорость ' + this.speed );
}Классы
Синтаксис для классов
class Название [extends Родитель] {
constructor
методы
}class User {
constructor(name) {
this.name = name;
}
sayHi() {
alert(this.name);
}
}
let user = new User("Вася");
user.sayHi(); // ВасяФункция constructor запускается при создании new User, остальные методы записываются в User.prototype.
class User {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// геттер
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
// сеттер
set fullName(newValue) {
[this.firstName, this.lastName] = newValue.split(' ');
}
};
let user = new User("Вася", "Пупков");
alert( user.fullName ); // Вася Пупков
user.fullName = "Иван Петров";
alert( user.fullName ); // Иван ПетровКласс, как и функция, является объектом. Статические свойства класса User – это свойства непосредственно User, то есть доступные из него «через точку».
Для их объявления используется ключевое слово static.
class User {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
static createGuest() {
return new User("Гость", "Сайта");
}
};
let user = User.createGuest();
alert( user.firstName ); // Гость
alert( User.createGuest ); // createGuest ... (функция)Как правило, они используются для операций, не требующих наличия объекта, например – для фабричных, как в примере выше, то есть как альтернативные варианты конструктора. Или же, можно добавить метод User.compare, который будет сравнивать двух пользователей для целей сортировки.
Также статическими удобно делать константы:
class Menu {
static get elemClass() {
return "menu"
}
}
alert( Menu.elemClass ); // menuclass Child extends Parent {
...
}class Animal {
constructor(name) {
this.name = name;
}
walk() {
alert("I walk: " + this.name);
}
}
class Rabbit extends Animal {
walk() {
super.walk();
alert("...and jump!");
}
}
new Rabbit("Вася").walk();
// I walk: Вася
// and jump!class Animal {
constructor(name) {
this.name = name;
}
walk() {
alert("I walk: " + this.name);
}
}
class Rabbit extends Animal {
constructor() {
// вызвать конструктор Animal с аргументом "Кроль"
super("Кроль"); // то же, что и Animal.call(this, "Кроль")
}
}
new Rabbit().walk(); // I walk: КрольООП в JavaScript
By Anna Protasevich
ООП в JavaScript
- 647