Обзор
Ключевое слово this
может быть одним из самых запутанных понятий в JavaScript. Мы объясним это this
с точки зрения непрофессионала с достаточным количеством примеров.
this
— это объект, свойством которого является функция
Один лайнер,
obj.someFunc(this);
Здесь this
находится в функции someFunc
, а someFunc
является свойством obj
. Итак, this
представляет obj
.
Примеры
Локальный объект
const myObject = { name: 'foo', task: function () { return `This is ${this.name} task.`; } }; myObject.task();
Это вернет текст This is foo task.
.
По определению здесь this
относится к объекту myObject
. Функция task
является собственностью myObject
.
Глобальный объект
В глобальной области this
— это объект window
.
В глобальной области видимости, если мы определим функцию
function a() { console.log(this); }
Он напечатает объект window
.
Поскольку метод a()
— это window.a()
, то, согласно нашему первому определению, для a()
this
является объектом window
.
«это» определяется тем, кто вызвал метод?
this
определяется объектом, называемым методом.
const a = function () { console.log('For a, this is ', this); const b = function () { console.log('For b, this is ', this); const c = { hi: function () { console.log('For c, this is ', this); } }; c.hi(); }; b(); }; a();
В этом случае и a
, и b
имеют объект window
как this
.
А для c
, поскольку он вызывается c
, this
является объектом c
.
Теперь вопрос может заключаться в том, как b()
имеет this
объект window
, а не a
. Поскольку b()
вызывается не так, как a.b()
. Вместо этого он вызывается как window.a(b())
"this" не имеет лексической области действия, она имеет динамическую область видимости
const obj = { name: 'Billy', sing() { console.log('a', this); var anotherMethod = function () { console.log('b', this); }; anotherMethod(); } }; obj.sing();
Здесь для a
контекст this
равен obj
.
Для b
контекст this
должен быть obj
. Но так как this
не имеет лексической области видимости и следует за вызовами методов, b
имеет this
контекст window
.
Вот почему this
следует за dynamic scoped
вместо lexical scoped
.
Сохранить этот контекст
Мы можем сохранить this
контекст,
- Сохранив его в переменной
- Используя метод стрелки
- Использование метода привязки (например, сначала привязка, а затем obj.prop()())
Чтобы решить эту проблему dynamic scope
, мы можем использовать файл arrow method
. Arrow method
привязан к lexical scoped
.
Использование метода стрелки
const obj = { name: 'Billy', sing() { console.log('a', this); var anotherMethod = () => { console.log('b', this); }; anotherMethod(); } }; obj.sing();
Здесь и a
, и b
имеют this
контекст obj
.
Использование привязки:
Другим было сделать this
до lexical scoped
методом bind()
const obj = { name: 'Billy', sing() { console.log('a', this); var anotherMethod = function () { console.log('b', this); }; return anotherMethod.bind(this); } }; obj.sing()();
Здесь a
и b
имеют this
контекст obj
.
Сохранение в лексическом объеме:
Удержание this
в другом объекте может использоваться для сохранения контекста this
.
const obj = { name: 'Billy', sing() { console.log('a', this); var self = this; var anotherMethod = function () { console.log('b', self); }; anotherMethod(); } }; obj.sing();
Здесь и a
, и b
имеют this
контекст obj
.
Манипулирование «этим»
Мы можем манипулировать/изменять ключевое слово this
, используя следующие методы:
- вызов
- применять
- связывать
Использование вызова
Мы можем использовать метод call
для вызова метода, подобного следующему:
var myMethod = function () {}; // following both statements are similar myMethod(); myMethod.call();
Мы будем использовать тот же механизм для ввода this
,
const wizard = { name: 'Wizard', health: 50, heal() { this.health = 100; } }; const archer = { name: 'Archer', health: 30 }; wizard.heal.call(archer); console.log(archer);
Это будет печатать,
{ name: 'Archer', health: 100 }
Здесь свойство здоровья такое же, как и у объекта wizard
.
Теперь давайте посмотрим на другой пример передачи параметров с помощью метода call
.
const wizard = { name: 'Wizard', health: 50, heal(param1, param2) { this.health = this.health + param1 + param2; } }; const archer = { name: 'Archer', health: 30 }; wizard.heal.call(archer, 10, 20); console.log(archer);
Это добавит параметры 10
и 20
с существующим значением 30
.
Таким образом, напечатанное значение равно
{ name: 'Archer', health: 60 }
Применить
apply()
похож на call()
, в основе лежит call()
. Единственная разница между call()
и apply()
заключается в том, что в apply()
параметр передается через круглые скобки.
var myMethod = function () {}; // following both statements are similar myMethod(); myMethod.apply();
Мы можем обновить/изменить this
с помощью apply
следующим образом:
const wizard = { name: 'Wizard', health: 50, heal(param1, param2) { this.health = this.health + param1 + param2; } }; const archer = { name: 'Archer', health: 30 }; wizard.heal.apply(archer, [10, 20]); console.log(archer);
Это напечатает то же значение, что и предыдущий метод call()
.
{ name: 'Archer', health: 60 }
Использование привязки
За исключением call()
и bind()
, bind()
не вызывает метод мгновенно. Вместо этого он возвращает метод, который можно вызвать позже.
const wizard = { name: 'Wizard', health: 50, heal(param1, param2) { this.health = this.health + param1 + param2; } }; const archer = { name: 'Archer', health: 30 }; const archerHeal = wizard.heal.bind(archer, 10, 20); archerHeal(); console.log(archer);
Это напечатает то же значение, что и предыдущий метод call()
или apply()
.
{ name: 'Archer', health: 60 }
Мы можем использовать bind
и получить интересный образец. Пусть у нас есть метод, который принимает два числа и возвращает умноженный результат.
const multiplyMethod = (num1, num2) => { return num1 * num2; };
Теперь с помощью функции bind мы создадим два метода из предыдущего метода. Оба метода предоставляют только один параметр.
- Один вернется умножить на 4
- Другой вернется, умноженный на 10
let multiplyByTwo = multiplyMethod.bind(this, 4); let multiplyByTen = multiplyMethod.bind(this, 10); console.log(multiplyByTwo(2)); console.log(multiplyByTen(2));
Это вернется,
8 20
Преимущества
Разрешить методам использовать свойства самого объекта
const myObject = { name: 'foo', task: function () { return `${this.name} task.`; }, doTask: function () { return `Do ${this.task()}`; } }; myObject.doTask();
Это вернет Do foo task.
Выполнять один и тот же код для нескольких объектов
function showMyName() { console.log(this.name); } const foo = { name: 'foo', showMyName }; const bar = { name: 'bar', showMyName }; foo.showMyName(); bar.showMyName();
Это вернет
foo bar
Логические
Заметим, пара примеров
Пример 01:
const myObj = { name: 'myName', myMethod() { console.log(this); } }; myObj.myMethod();
Здесь this
— это и есть myObj
.
Пример 02:
const myObj = { name: 'myName', myMethod() { return function () { return console.log(this); }; } }; myObj.myMethod()();
Поскольку функция возврата не вызывается myObj
, здесь объект this
является window
. Он использует dynamic scope
вместо lexical scope
.
Пример 03:
const myObj = { name: 'myName', myMethod() { return () => { return console.log(this); }; } }; myObj.myMethod()();
Поскольку arrow method
строго поддерживает lexical scope
, здесь this
представляет myObj
.