Skip to content
garevna edited this page Nov 1, 2018 · 14 revisions

🎓 Классы

ES6 ( ECMAScript 2015 )

Синтаксический сахар на поверхности прототипной модели наследования

class declaration class expression
Потеря контекста static
get & set extends
super Пример

⚠️ Код внутри тела класса всегда выполняется в strict mode
( даже если вы не использовали директиву **_'use strict'_** )

⚠️ "Тело" класса всегда заключено в фигурные скобки { }

Внутри фигурных скобок объявляются конструктор ( constructor ) и методы класса

Метод constructor создает и инициализирует экземпляра класса

⚠️ Все собственные свойства экземпляра должны быть объявлены в конструкторе constructor()

"Под капотом" работает все та же прототипная модель наследования JS

Создаваемые в конструкторе класса свойства и методы могут быть приватными и публичными
( как и в обычном конструкторе )

В обычном конструкторе контекстом вызова приватных методов будет глобальный объект window

В конструкторе класса приватные методы теряют контекст ( undefined )

class User {
    constructor ( name ) {
        var privateVar = prompt ( "Set privateVar value: " )
        function showPrivate () {
            console.log ( `Ай-яй-яй, у меня контекст вызова ${this}` )
            console.log ( `Зато я вижу приватную переменную: ${privateVar}` )
        }
        this.name = name || "Бегемот"
        this.show = function () {
            showPrivate ()
        }
    }
}

var user = new User ( "Крокодил" )
user.show()
Result
Ай-яй-яй, у меня контекст вызова undefined
Зато я вижу приватную переменную: 789

Для того, чтобы избавиться от иллюзий по поводу "классов" в JS,
создадим аналогичный экземпляр с помощью обычного конструктора

function User ( name ) {
    var privateVar = prompt ( "Set privateVar value: " )
    function showPrivate () {
        console.log ( `Ай-яй-яй, у меня контекст вызова ${this}` )
        console.log ( `Зато я вижу приватную переменную: ${privateVar}` )
    }
    this.name = name || "Бегемот"
    this.show = function () {
        showPrivate ()
    }
}

var user = new User ( "Крокодил" )
user.show()
Result
Ай-яй-яй, у меня контекст вызова [object Window]
Зато я вижу приватную переменную: 789

Выведем в консоль оба варианта user и найдем те косметические отличия, которые там должны быть 😉

  • constructor: class User / constructor: ƒ User( name )

🎓 class declaration

🚫 hoisting

объявление класса дожно быть раньше первого обращения к нему

Классы - это специальные функции-"обертки", в которые "заворачивают" конструктор

☕ 1️⃣
class Picture {
    constructor ( url, width ) {
        this.elem = document.createElement ( 'img' )
        this.elem.src = url
        this.width = width
    }
}

typeof Picture  // "function"

⚠️ объявленный класс невозможно удалить динамически, без перезагрузки страницы
В примере 1️⃣ идентификатор Picture уже занят, и никакие магические заклинания не помогут переопределить его содержание

⚠️ если обычный конструктор JS можно вызвать и как функцию, и как конструктор ( с ключевым словом new ), то конструктор класса вызвать без ключевого слова new нельзя - будет сгенерировано исключение TypeError

let x = new Picture ( "http://www.radioactiva.cl/wp-content/uploads/2018/05/pikachu.jpg", 200 )
document.body.appendChild ( x.elem )

🎓 class expression

еще один способ определить класс
class expression может быть именованным или аниномным

Примеры именованных классов

☕ 2️⃣
let Picture = class {
    constructor () {
        this.canvas = document.createElement ( 'canvas' )
        document.body.appendChild ( this.canvas )
        this.area = this.canvas.getContext ( "2d" )
    }
}

Picture.name   // "Picture"
☕ 3️⃣
let Picture = class Canvas {
    constructor () {
        this.canvas = document.createElement ( 'canvas' )
        document.body.appendChild ( this.canvas )
        this.area = this.canvas.getContext ( "2d" )
    }
}

Picture.name   // "Canvas"
  • Имя class expression является локальным для тела класса
  • Оно доступно как свойство name класса
  • Из экземпляра picture класса можно получить имя класса так:
picture.__proto__.constructor.name   // "Canvas"

Пример анонимного класса

☕ 4️⃣
let pict = new class {
    constructor () {
        this.canvas = document.createElement ( 'canvas' )
        document.body.appendChild ( this.canvas )
        this.area = this.canvas.getContext ( "2d" )
    }
} ()

здесь мы создаем экземпляр pict анонимного класса

pict.__proto__.constructor.name  // ""
☕ 5️⃣
const Sample = class Canvas {
    constructor () {
        this.canvas = document.createElement ( 'canvas' )
        document.body.appendChild ( this.canvas )
        this.resizeCanvas()
        this.canvas.style.border = "1px solid #000000"
        this.area = this.canvas.getContext ( "2d" )
    }
    resizeCanvas ( event ) {
        this.canvas.width = window.innerWidth - 30
        this.canvas.height = window.innerHeight - 20
    }
    drawLine ( points ) {
        this.area.moveTo( points[0].x, points[0].y )
        this.area.lineTo( points[1].x, points[1].y )
        this.area.stroke()
    }
}

let pict = new Sample ()
window.onresize = pict.resizeCanvas.bind ( pict )
pict.drawLine ( [ { x: 50, y: 50 }, { x: 250, y: 250 } ] )
pict.drawLine (  [ { x: 250, y: 250 }, { x: 100, y: 250 } ] )

📌 Чтобы получить имя класса, нужно использовать его свойство name:

console.log ( Sample.name ) // "Canvas"

🎓 Потеря контекста

📌 В строгом режиме не происходит неявной передачи контекста вызова

⚠️ Потеря контекста происходит всегда, если ссылка на метод передается в новую переменную:

☕ 6️⃣
     let drawLine = pict.drawLine
     drawLine ( [ { x: 50, y: 50 }, { x: 250, y: 250 } ] )

будет сгенерировано исключение:

⛔️ Uncaught TypeError: Cannot read property 'area' of undefined

Передачу контекста вызова нужно сделать явным образом:

let drawLine = pict.drawLine.bind ( pict )

📌 Потеря контекста ( undefined ) происходит вследствие того, что весь код внутри тела класса выполняется в strict mode, хотя явного указания 'use strict' в коде класса нет

При отсутствии явного указания на объект, вызывающий метод,
в строгом режиме this не будет ссылкой на глобальный объект window
В строгом режиме this будет undefined


☕ 7️⃣

В этом примере контекст теряется в функции getProp(),
объявленной внутри метода addSomeInfo
( внутренняя функция не наследует контекст вызова родительской )

class User {
    constructor ( name ) {
        this.name = name || 'unknown'
    }
    addSomeInfo ( props ) {
        if ( !Array.isArray ( props ) ) return
        function getProp ( prop ) {
            this [ prop.name ] = prop.value
        }
        for ( var prop of props ) {
            getProp ( prop )
        }
    }
}

Создадим экземпляр user класса User
и вызовем метод addSomeInfo
в контексте объекта user

var user = new User ( "Grig" )
user.addSomeInfo ([
    { name: "age", value: 25 },
    { name: "hobby", value: [ "football", "fishing" ] }
])
🙀 Результат
⛔️ Uncaught TypeError: Cannot set property 'age' of undefined

❗ Внутри функции getProp контекст вызова ( this ) оказался undefined

Теперь используем стрелочную функцию getProp, которая не теряет контекст 😉

class User {
    constructor ( name ) {
        this.name = name || 'unknown'
    }
    addSomeInfo ( props ) {
        if ( !Array.isArray ( props ) ) return
        var getProp = prop => this [ prop.name ] = prop.value
        for ( var prop of props ) {
            getProp ( prop )
        }
    }
}

Создадим экземпляр user и вызовем метод addSomeInfo

var user = new User ( "Grig" )
user.addSomeInfo ([
    { name: "age", value: 25 },
    { name: "hobby", value: [ "football", "fishing" ] }
])
console.log ( user )
🙀 Результат
▼ User {name: "Grig", age: 25, hobby: Array(2)}
    age: 25
  ► hobby: (2) ["football", "fishing"]
    name: "Grig"
  ▼ __proto__:
      ► addSomeInfo: addSomeInfo ( props ) { if ( !Array.isArray ( props ) ) return var getProp = prop => {…}
      ► constructor: class User
      ► __proto__: Object


© Irina H.Fylyppova 2018
Использование данных материалов или любой их части коммерческими школами ( курсами ) является нарушением авторских прав


Новая версия


1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19

Занятие 1

⤵️

Занятие 2

⤴️ ⤵️

Занятие 3

⤴️ ⤵️

Занятие 4

⤴️ ⤵️

Занятие 5

⤴️ ⤵️

Занятие 6

⤴️ ⤵️

Занятие 7

⤴️ ⤵️

Занятие 8

⤴️ ⤵️

Занятие 9

⤴️ ⤵️

Занятие 10

⤴️ ⤵️

Занятие 11

⤴️ ⤵️

Занятие 12

⤴️ ⤵️

Занятие 13

⤴️ ⤵️

Занятие 14

⤴️ ⤵️

Занятие 15

⤴️ ⤵️

Занятие 16

⤴️ ⤵️

Занятие 17

⤴️ ⤵️

Занятие 18

⤴️ ⤵️

Занятие 19

⤴️ ⤵️

⤴️

ico20 Дополнительно
dir-20 Справочная инфо

Clone this wiki locally