this в JavaScript

Содержание

this - это ключевое слово, которое ссылается на тот или иной объект в зависимости от того, где оно записано или как была вызвана функция, где this присутствует.

this или другими словами контекст выполнения - это одна из тем понимание которой необходимо для изучения объектно-ориентированного программирования (ООП) в JavaScript.


Синтаксис

  
  this
  

Посмотрим чему равен this с помощью метода console.log()

  
  console.log(this) // this === window
  

или запросим значение свойства объекта, предварительно установив его, как this.

  
  let student = {
    name: this
  }

  console.log(student.name) // this === window
  

В глобальном контексте, за пределами функций this равен объекту window в независимости от того, какой режим разработки установлен строгий 'use strict' или нестрогий.

Кстати, уже здесь мы можем обратиться не только к объекту window в целом, но также и к его свойствам.

  
  console.log(this.onclick) // null
  

this в функциях

Если this записывать в отдельной функции, то его значение не всегда равно window. В нестрогом режиме всё без сюрпризов.

  
  function testThis() {
    console.log(this)
  }
  
  testThis() // в браузере window
  testThis() // в Node global
  

В строгом режиме.

  
  'use strict'

  function testThis() {
    console.log(this)
  }
  
  testThis() // undefined
  

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

  
  function testThis() {
    (function () {
      console.log(this === window)
    })()
  }
  
  testThis() // true
  

Методы объекта и this

Теперь рассмотрим другой контекст отличный от глобального.

  
  let student = {
    name: 'Дмитрий',
    course: 'HTML + CSS',
    level: 'junior',
    objMethod() {
      console.log(this)
    }
  }
  
  student.objMethod()
  

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

  
  let student = {
    name: 'Дмитрий',
    course: 'HTML + CSS',
    level: 'junior',
    statement() {
      console.log(`${this.name} c уровнем ${this.level} хочет поступить на курс ${this.course}`)
    }
  }
  
  student.statement()
  

Таким образом, мы можем обращаться через this к значениям свойств объекта через их имена. В принципе запись ниже аналогична варианту с this.

  
  let student = {
    name: 'Дмитрий',
    course: 'HTML + CSS',
    level: 'junior',
    statement() {
      console.log(`${student.name} c уровнем ${student.level} хочет поступить на курс ${student.course}`)
    }
  }
  
  student.statement()
  

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

  
  function statement() {
    console.log(`${this.name} c уровнем ${this.level} хочет поступить на курс ${this.course}`)
  }
  
  let student1 = {
    name: 'Дмитрий',
    course: 'HTML + CSS',
    level: 'junior',
    statement
  }
  
  let student2 = {
    name: 'Ольга',
    course: 'Basic JavaScript',
    level: 'junior',
    statement
  }
  
  student1.statement() // Дмитрий c уровнем junior хочет поступить на курс HTML + CSS
  student2.statement() // Ольга c уровнем junior хочет поступить на курс Basic JavaScript
  

Имея в арсенале this нет надобности писать для каждого объекта одну и ту же функцию, в примере мы записали statement() один раз, а далее назначили ее двум разным объектам. При вызове метода для student1 и student2 получили корректный результат.

Работая с this стоит помнить, что ключевое слово определяется в момент вызова функции. Таким образом, если функцию положить в переменную и вызвать уже ее, результат будет отличным.

  
  let student1 = {
    name: 'Пётр',
    statement() {
        console.log(`Имя студента ${this.name}`)
    }
  }
  
  student1.statement() // Имя студента Дмитрий
  let anotherVar = student1.statement
  anotherVar() // Имя студента
  

Стрелочные функции

Стрелочная функция не имеют собственного контекста и если в таком случае прописать this то ссылаться ключевое слово будет на объект ближайший по иерархии. Такая особенность удобна, когда мы хотим передать в функцию внешний контекст.

  
  let student = {
    name: 'Дмитрий',
    objMethod() {
        console.log(this) // {name: 'Дмитрий', objMethod: ƒ}

        let arrowFunc = () => console.log(this)
        arrowFunc() // {name: 'Дмитрий', objMethod: ƒ}

        function funcWithinFunc() {
            console.log(this)
        }
        funcWithinFunc() // window
    }
  }
  
  student.objMethod() // student
  

В первых двух случаях this ссылается на значение объекта student, в свою очередь this, записанная в функции funcWithinFunc, будет равна window.

Функция-конструктор

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

  
  function Student(name, course) {
    this.name = name
    this.course = course
    this.school = 'LearnJS'
  }

  let student1 = new Student('Вера', 'JavaScript Basic')
  console.log(student1)
  

Методы call() и apply()

call() и apply() позволяют явно настроить контекст выполнения функции, результатом работы методов будет выполнение этой функции. Методы идентичны между собой, единственное их различие, это формат в котором записываются аргументы, для call() через запятую, для appy() в массиве.

  
  let student1 = {name: 'Степан'}
  let student2 = {name: 'Юлия'}
  let sayName = function(course) {
      console.log(`${this.name} изучает ${course}`)
  }

  sayName.call(student1, 'JS') // Степан
  sayName.apply(student2, ['HTML + CSS']) // Юлия
  

В первом случае мы определили контекст выполнения функции sayName относительно объекта student1, во втором относительно student2 и получили в console Степан и Юлия соответственно.

Метод bind()

bind() – это встроенный метод, который также даёт возможность зафиксировать контекст выполнения, чтобы заранее быть уверенным, какое значение будет у this. В отличие от call() и apply() метод bind() возвращает новую функцию, а не вызывает изначальную.

  
  let student1 = {name: 'Степан'}
  let student2 = {name: 'Юлия'}
  let sayName = function() {
      console.log(this.name)
  }

  sayName.bind(student1)() // Степан
  sayName.bind(student2)() // Юлия
  

Итого

1. Ключевое слово this позволяет работать с контекстом, получая доступ до объектов, а также имеет в арсенале методы, для того, чтобы менять этот контекст.

2. this не является фиксированным и определяется во время выполнения кода. Таким образом, пока функция не вызвана this не имеет значения.

3. Стрелочные функции не имеют собственного контекста и связываются с ближайшим по иерархии.

4. bind(), call() и apply() - методы позволяющие управлять контекстом.

5. При вызове функции-конструктора this принимает значение вновь созданного объекта.