Що лежить в основі цієї ідіоми JavaScript: var self = this?


357

У джерелі для демонстрації нотаток зберігання SQL WebKit HTML 5 SQL я бачив таке :

function Note() {
  var self = this;

  var note = document.createElement('div');
  note.className = 'note';
  note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
  note.addEventListener('click', function() { return self.onNoteClick() }, false);
  this.note = note;
  // ...
}

Автор використовує себе в деяких місцях (функція тіла) і це в інших місцях (тіла функцій , визначені в списку аргументів методів). Що відбувається? Тепер, коли я помітив це один раз, я почну його бачити всюди?


4
Це особливість мови JS під назвою "лексичне закриття".
subZero

2
Можливий дублікат: var self = this? .
DavidRR

Поняття ЦЕ ТО
AL-zami

Відповідний приклад у цій відповіді stackoverflow.com/a/20279485/5610569 (на питання "Як отримати доступ до правильного thisвсередині зворотного дзвінка?")
FluxLemur

Відповіді:


431

Дивіться цю статтю на alistapart.com . (Ред.: Стаття оновлювалася з моменту посилання)

selfвикористовується для підтримки посилання на оригінал, thisнавіть коли контекст змінюється. Це техніка, яка часто використовується в обробниках подій (особливо при закритті).

Редагувати: Зауважте, що використання selfзараз відсторонено, як window.selfіснує, і може бути причиною помилок, якщо ви не будете обережні.

Те, що ви називаєте змінною, не має особливого значення. var that = this;добре, але в назві немає нічого магічного.

Функції, оголошені всередині контексту (наприклад, зворотні виклики, закриття), матимуть доступ до змінних / функцій, оголошених у тому самому діапазоні або вище.

Наприклад, простий зворотний виклик події:

function MyConstructor(options) {
  let that = this;

  this.someprop = options.someprop || 'defaultprop';

  document.addEventListener('click', (event) => {
    alert(that.someprop);
  });
}

new MyConstructor({
  someprop: "Hello World"
});


Здається, що стаття потрапила до використанняvar that = this;
Боб Штейн

@BobStein Дякую Відповідь я оновлю відповідно.
Джонатан

96

Я думаю, що назва змінної "self" більше не повинна використовуватися таким чином, оскільки сучасні браузери надають глобальну змінну, щоself вказує на глобальний об'єкт або звичайного вікна, або WebWorker.

Щоб уникнути плутанини та потенційних конфліктів, ви можете написати var thiz = thisабо var that = thisзамість цього.


43
Я зазвичай використовую_this
djheru

6
@djheru +1. настільки приємніше, ніж " that" (до чого мій мозок ніколи не звикне).
o_o_o--

9
Я почав використовувати «я» :)
Mopparthy Ravindranath

3
Поки сучасні браузери не почнуть надавати глобальну змінну _ це, це чи я.
Beejor

10
З використанням імені абсолютно немає проблем self, якщо ви оголосите його як varпридатне для використання, воно затьмарить глобальний. Звичайно, якщо ви забули, varтоді це не працюватиме і з іншим ім'ям.
Бергі

34

Так, ви побачите це всюди. Це часто that = this;.

Подивіться, як selfвикористовується всередині функцій, викликаних подіями? У них був би свій власний контекст, тому selfвін використовується для утримання того, thisщо вступило Note().

Причина selfвсе ще доступна для функцій, хоча вони можуть виконуватися лише після того, як Note()функція закінчиться, полягає в тому, що внутрішні функції отримують контекст зовнішньої функції через закриття .


12
Для мене суттєвим моментом є те, що selfне має особливого значення. Я особисто вважаю за краще використовувати вар, названий чимось іншим, ніж selfте, що мене часто бентежить, оскільки я очікую, що "Я" є зарезервованим словом. Тож мені подобається ваша відповідь. І в прикладі ОП я б віддав перевагу var thisNote = thisчи подібне.
steve

@steve погодився, хоча я намагаюся уникати використання цього / само посилань взагалі, оскільки вони дуже крихкі з точки зору ремонту.
mattLummus

28

Слід також зазначити, що існує альтернативна схема проксі для підтримки посилання на оригінал thisу зворотному дзвінку, якщо ви не любите var self = thisідіому.

Оскільки функцію можна викликати із заданим контекстом, використовуючи function.applyабо function.call, ви можете написати обгортку, яка повертає функцію, яка викликає вашу функцію applyабо callвикористовує заданий контекст. Дивіться proxyфункцію jQuery для реалізації цього шаблону. Ось приклад його використання:

var wrappedFunc = $.proxy(this.myFunc, this);

wrappedFuncпотім можна викликати і матиме вашу версію thisяк контекст.


10

Як пояснили інші, var self = this;дозволяє коду в закритій частині повернутися до батьківської області.

Однак зараз 2018 рік і ES6 широко підтримується всіма основними веб-браузерами. var self = this;Ідіома не настільки важливо , як це було раніше.

Зараз це можливо уникнути var self = this;за допомогою використання стрілочних функцій .

У випадках, коли ми використовували б var self = this:

function test() {
    var self = this;
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", function() {
        console.log(self.hello); // logs "world"
    });
};

Тепер ми можемо використовувати функцію стрілки без var self = this:

function test() {
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", () => {
        console.log(this.hello); // logs "world"
    });
};

Функції стрілки не мають своєї власної thisі просто припускають область, що додається.


Або - шок, жах! - чому б не передати фактичну відповідну річ як аргумент своїй функції (закриття)? Чому, на пекло, ви посилаєтесь поза державою, чому чорт хто програмує так? Ніколи не існує жодної реальної причини для цього. Натомість .addEventListender("click", (x) => { console.log(x); });Ви пояснили, як і чому дуже чітко, і я погоджуюся, що використання стрілочних функцій має більше сенсу, але все ж ... це просто жахливо, ліниво, безладно, програмування.
Бенджамін Р

2
У JavaScript посилання на батьківський обсяг є надзвичайно поширеним та необхідним. Це основна частина мови. Ваша пропозиція передати в батьківській області як аргумент обробника подій насправді неможлива. Крім того, в ES6 функції стрілок використовують лексичне масштабування - "це" посилається на його поточне оточуюче поле і далі - це не "посилання поза станом дії" чи щось подібне.
Елліот Б.

9

Змінна фіксується вбудованими функціями, визначеними методом. thisу функції буде посилатися на інший об’єкт. Таким чином, ви можете змусити функцію утримувати посилання на thisзовнішню область.


9

Це химерність JavaScript. Коли функція є властивістю об'єкта, більш доречно називається методом, це стосується об'єкта. У прикладі обробника подій об'єктом, що містить, є елемент, який ініціював подію. Коли стандартна функція викликається, це буде ставитися до глобального об'єкту. Якщо ви вкладете функції, як у вашому прикладі, це взагалі не стосується контексту зовнішньої функції. Внутрішні функції поділяють сферу дії з функцією, що містить функцію, тому розробники будуть використовувати варіанти var that = this, щоб зберегти це, що їм потрібно у внутрішній функції.


5

Насправді "я" - це посилання на window ( window.self), тому, коли ви говорите, що var self = 'something'ви переосмислюєте посилання на вікно на себе - тому що "self" існує в об'єкті window.

Ось чому більшість розробників віддають перевагу var that = thisпонадvar self = this;

Все одно; var that = this;не відповідає належній практиці ... припускаючи, що ваш код буде переглянуто / змінено пізніше іншими розробниками, ви повинні використовувати найпоширеніші стандарти програмування стосовно спільноти розробників

Тому вам слід використовувати щось на зразок var oldThis/ var oThis/ тощо - щоб було зрозуміло у вашому масштабі // .., це не так вже й багато, але заощадить кілька секунд та кілька циклів мозку


2
@prior Я думаю, що це має сенс до останнього абзацу.
miphe

0

Як уже згадувалося кілька разів вище, "self" просто використовується для збереження посилання на "this" перед входом у функцію. Опинившись у функції 'це' посилається на щось інше.


@JohnPaul ... це робить дати відповідь. Це може бути не правильною відповіддю, але як ви можете сказати, що "це звикло ..." - це не відповідь на те, "чому він це зробив"?
GreenAsJade

-1
function Person(firstname, lastname) {
  this.firstname = firstname;

  this.lastname = lastname;
  this.getfullname = function () {
    return `${this.firstname}   ${this.lastname}`;
  };

  let that = this;
  this.sayHi = function() {
    console.log(`i am this , ${this.firstname}`);
    console.log(`i am that , ${that.firstname}`);
  };
}

let thisss = new Person('thatbetty', 'thatzhao');

let thatt = {firstname: 'thisbetty', lastname: 'thiszhao'};

thisss.sayHi.call (thatt);


1
Вам слід додати пояснення з кодом, що ви зробили спеціально.
Фархана
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.