Преглед
Ключовата дума 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())
„това“ не е с лексикален обхват, той е с динамичен обхват
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
.