OOP
Skip to Classes
Prototype
- klíčové slovo:
__proto__
- vlastnost konkrétní instance
- objekt má svoji hidden vlastnost Prototype, která je buď
null
nebo referencuje jiný objekt (který nastavím) - pokud na objektu volám nějaké metody nebo properties a ony chybí, JS se automaticky dívá na Prototype, jestli tam nejsou
const o1 = { a: 1 }
const o2 = { b: 3 }
o2.__proto__ = o1 // sets o2.[[Prototype]] = o1
console.log(o2.a) // inherited
console.log(o2.b) // own
// lze také nastavit takto
let animal = {
eats: true,
walk() {
alert("Animal walk!");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
- řetězec Prototype může být delší, ale nesmí jít do kruhu
__proto__
se už moc nepoužívá (je to getter/setter na Prototype)- spíš se používá
Object.getPrototypeOf/Object.setPrototypeOf
- spíš se používá
- Javascript - Context (this) - není vůbec ovlivněn prototypy
- context je jednoduše toho objektu, který je před “tečkou” při volání metody a je jedno jestli je to ten daný objekt nebo jeho prototyp
- takže v případě setterů na prototypu, pokud je volám “z potomka”, tak ovlivňuji state jenom toho potomka, ne toho prototypu
Rabbit.prototype = animal; // když se vytvoří nový objekt Rabbit, přiřaď mu prototype animal
- každá funkce má svůj defaultní prototype, ten je přiřazený sám sobě viz:
F.prototype = { constructor: F}
F.prototype
nastaví Prototype nového objektu, když se zavolánew F()
// komentare jsou, co vsechno to udela, kdyz zavolam 'new User(...)'
function User(name, age) {
// this = {}
// this.constructor = User
// this.[[class]] = User
// this.__proto__ = User.prototype
this.name = name
this.age = age
// return this
}
User.prototype = {
// zdedena vlastnost
sayName: function() {
console.log(this.name)
}
}
const User = new User('Olda', 28)
function Human(name, age) {
this.name = name
this.age = age
}
Human.prototype = {
sayName: function() {
console.log(this.name)
}
}
function FPlayer(name, age, team) {
// toto je dedeni own vlastnosti
Human.call(this, name, age) // zavolej funkci Human s contextem FPlayera, takze v Human() bude this context context tohoto FPlayer
this.team = team
}
FPlayer.prototype = {
sayTeam: function() {
console.log(this.team)
}
}
// toto je dedeni prototypu
// Object.create() vytvori novou (prazdnou instanci) s prototypem Human.prototype a vrátí ji
// tu "prázdnou" instanci s prototypem Humana si přiřadím k FPlayeru a pak už ji mohu modifikovat
// - kdybych modifikoval přímo, tak budu přidávat sayTeam i k Humanům, kteří nejsou FPlayer a to nechci
FPlayer.prototype = Object.create(Human.prototype)
FPlayer.prototype.sayTeam = function() {
console.log(this.team)
}
Objekty jako klasické objekty, Array, Date, Function mají své native prototypes, které se vytvoří a přiřadí automaticky, jakmile je vytvořím (pomocí new Object(), new Array() atd.) - ty metody nemusím přímo volat, volají se i interně (například, když vytvořím [1, 2, 3], nebo {})
Object.create()
- vytváření objektů bez řešení
__proto__
let animal = {
eats: true
};
let rabbit = Object.create(animal); // stejné jako rabbit.__proto__ = animal
console.log(Object.getPrototypeOf(rabbit) === animal); // true
Object.setPrototypeOf(rabbit, {}); // změnil jsem mu prototype na {}
// stejně tak mohu při Object.create() rovnou přiřadit properties navíc
let donkey = Object.create(animal, {
jumps: {
value: false
}
});
alert(rabbit.jumps); // false
Pomocí Object.create se dá také efektivně kopírovat:
// vytvorime (shallow) kopii objektu obj
let clone = Object.create(
Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)
)
Classes
= šablona pro vytváření objektů, má member variables a member functions = methods
Řešení pomocí moderního JS
Třída je vlastně taková převlečená funkce, ale není to jenom syntaktický sugar
class Human {
// toto je take definovano na prototypu
constructor(name, age) {
this.name = name
this.age = age
}
// tato metoda se definuje na prototypu
sayName() {
console.log(this.name);
}
}
let human = new Human("John", 34); // automaticky zavolá i constructor()
// pomocí extends se rozšíří i prototyp
class FPlayer extends Human {
constructor(name, age, team) {
super(name, age) // moderne zapsane Human.call(name, age)
this.team = team
}
sayTeam() {
console.log(this.team)
}
}
- mezi třídními metodami nejsou potřeba středníky
- vše uvnitř třídy je
use strict
- gettery, settery:
class User {
constructor(name) {
this._name = name; // toto zavola setter
}
get name() {
return this._name;
}
set name(value) {
if(value.length < 4) {
alert("Name is too short");
return;
}
this._name = value;
}
}
let user = new User("John");
alert(user.name); //John
user = new User(""); // Name is too short
- relativní novinka v JS jsou class props - ale jsou to individuální objekty, nejsou na prototypu
class User {
name = "John";
}