Class super - garevna/js-course GitHub Wiki
| ⏪ |
|---|
Методы родительского класса доступны в дочернем классе посредством ключевого слова super
Расширим метод drawLine родительского класса,
добавив аргумент lineWidth ( толщину линии )
Для этого определим "расширенный" метод drawLine
внутри дочернего класса,
который будет вызывать метод drawLine
родительского класса
с помощью ключевого слова super:
super.drawLine ( points, lineColor )Теперь код будет таким:
const Canvas = class {
constructor () {
this.canvas = document.createElement ( 'canvas' )
document.body.appendChild ( this.canvas )
this.canvas.style.border = "1px solid #000000"
this.area = this.canvas.getContext ( "2d" )
}
drawLine ( points ) {
this.area.beginPath()
this.area.moveTo( points[0].x, points[0].y )
this.area.lineTo( points[1].x, points[1].y )
this.area.stroke()
}
}
class ExtendedCanvas extends Canvas {
drawLine ( points, lineColor, lineWidth ) {
this.area.lineWidth = lineWidth || 3
this.area.strokeStyle = lineColor
super.drawLine ( points )
}
}Создадим экземпляр дочернего класса:
let newCanvas = new ExtendedCanvas ()и вызовем его метод drawLine
newCanvas.drawLine ( [ { x: 20, y: 20 }, { x: 300, y: 400 } ], "#ffaa00", 10 )Теперь линия будет отрисовываться заданной толщины
В предыдущих примерах мы не использовали конструктор наследующего класса
☝ Когда нужно добавить свойства
экземпляру наследующего класса,
без конструктора это сделать невозможно
✋ Первое, что нужно выполнить в конструкторе наследующего класса - вызвать метод super ()
const Canvas = class {
constructor () {
this.canvas = document.createElement ( 'canvas' )
document.body.appendChild ( this.canvas )
this.area = this.canvas.getContext ( "2d" )
}
drawLine ( points ) {
this.area.beginPath()
this.area.moveTo( points[0].x, points[0].y )
this.area.lineTo( points[1].x, points[1].y )
this.area.stroke()
}
}
class ExtendedCanvas extends Canvas {
constructor () {
super ()
this.history = []
}
}В противном случае будет сгенерировано исключение:
⛔️ Uncaught ReferenceError:
Must call super constructor in derived class before accessing 'this' or returning from derived constructor
Ключевое слово super можно использовать без объявления классов
Он является просто ссылкой на прототип объекта
Поэтому можно использовать его для доступа к свойствам и методам объекта-прототипа
В примерах далее мы будем использовать объекты, объявленные в литеральной форме
В качестве прототипа объекта person будет выступать объект human
Назначать объект human прототипом объекта person мы будем с помощью метода
Object.setPrototypeOf ( person, human )После такого назначения внутри объекта person
свойства и методы объекта human будут доступны
с помощью ключевого слова super
В следующем примере вызовем методы place () и say ()
прототипа human
в методах getPlace () и talk ()
объекта person
с помощью ключевого слова super :
let human = {
place () {
var x = document.createElement ( "p" )
document.body.appendChild ( x )
x.id = "demo"
return x
},
say ( text ) {
this.place.innerHTML = text
}
}
let person = {
getPlace () { this.place = super.place () },
talk ( text ) {
super.say ( text )
}
}
Object.setPrototypeOf ( person, human )
person.getPlace ()
person.talk ( "привет!" )✍ В этом примере метод place() прототипа ( объекта human )
проверяет наличие элемента с id === "demo",
и если такой элемент найден,
возвращает ссылку на него,
в противном случае создает такой элемент,
добавляет его на страницу и возвращает ссылку на него
✍ Объект person изначально не имеет свойства place,
но имеет собственный метод getPlace (),
который создает такое свойство,
вызывая с помощью ключевого слова super
метод place() прототипа ( объекта human ),
и присваивая возвращенное этим методом
значение собственному свойству place
✍ Метод talk( text ) объекта person
вызывает метод getPlace()
до вызова метода say ()
прототипа ( объекта human )
let human = {
place: () =>
document.getElementById ( "demo" ) ?
document.getElementById ( "demo" ) :
document.body.appendChild (
document.createElement ( "p" )
).id = "demo",
say ( text ) {
this.place.innerHTML = text
}
}
let person = {
getPlace () { this.place = super.place() },
talk ( text ) {
this.getPlace ()
super.say ( text )
}
}
Object.setPrototypeOf ( person, human )
person.talk ( "привет!" )
setTimeout ( () => person.talk ( "Hello, baby!" ), 2000 )☝ Обратите внимание,
что при объявлении метода place() объекта human
мы использовали стрелочную функцию,
а при объявлении метода say ее использовать нельзя,
поскольку внутри методов,
объявленных с помощью стрелочных функций,
контектом вызова будет глобальный объект window
В этом примере свойство place прототипа ( объекта human )
уже не является методом
Его значение ( ссылка на элемент )
будет установлено при инициализации объекта human
В этом примере демонстрируется взаимозаменяемость
ключевых слов super и this
при ссылках на свойства прототипа
👉 Метод talk() объекта person
вызывает метод say () прототипа
без ключевого слова super
( с ключевым словом this )
👉 Когда метод say() в объекте person не будет найден,
поиск будет продолжен в прототипе,
где и будет благополучно найден
👉 Внутри метода say(), вызванного из метода talk(),
контекстом вызова будет объект person
( т.е. this будет указывать на объект person )
но тем не менее ссылка this.place
будет благополучно разрешена по цепочке прототипов
let human = {
place: ( () => {
if ( document.getElementById ( "demo" ) )
return document.getElementById ( "demo" )
var x = document.body.appendChild (
document.createElement ( "p" )
)
x.id = "demo"
return x
})(),
say ( text ) {
this.place.innerHTML = text
}
}
let person = {
talk ( text ) {
this.say ( text )
}
}
Object.setPrototypeOf ( person, human )
person.talk ( "привет!" )
setTimeout ( () => person.talk ( "Hello, baby!" ), 2000 )👉 Если же имена свойств объекта и его прототипа совпадают,
и нужно вытянуть именно свойство прототипа,
а не собственное свойство объекта,
то для унаследованных свойств можно использовать __proto__
let person = {
say ( text ) { console.log ( text ) },
talk ( text ) {
this.__proto__.say ( text )
}
}👉 Очевидно, в таком случае код:
super.say ( text )короче, чем
this.__proto__.say ( text )а результат идентичный 😉
👉 В этом примере мы используем геттеры и сеттеры свойств объектов
Для вычисляемых свойств это наиболее корректный способ доступа к их значениям
let human = {
id: "",
get place () {
if ( this.id ) return document.getElementById ( this.id )
},
set place ( newId ) {
this.id = newId
if ( document.getElementById ( this.id ) ) return
var x = document.createElement ( "p" )
document.body.appendChild ( x )
x.id = this.id
},
get message () {
return this.place.innerHTML
},
set message ( val ) {
this.place.innerHTML = val
}
}
let person = {
talk ( text ) {
super.message = text
},
get place () {
return super.place
},
set place ( newId ) {
super.place = newId
}
}
Object.setPrototypeOf ( person, human )
person.place = "demo"
person.talk ( "привет!" )
setTimeout ( () => person.talk ( "Hello, baby!" ), 2000 )| ⏪ |
|---|