Is var self = це; поганий зразок?


77

Я відчуваю, що мені потрібно:

var self = this;

багато в моїх "класах" javascript. Хоча це зазвичай роблять, це здається трохи неправильним. Те, що я сподіваюся знайти в цьому питанні, - кращий спосіб вирішити це питання або щось, що переконає мене, що це цілком добре.

Це стандартний спосіб зберігати правильні прив’язки? Чи повинен я стандартизувати використання "self" скрізь, якщо явно не потребую "this".

редагувати : Я точно знаю, навіщо мені це потрібно, мені просто цікаво, чи вважається це трохи злим і чому. Мені відомо, що існує також вбудована функція javascript 'apply', щоб явно визначити область дії під час виклику методу. Чи краще?


2
Я завжди використовую var that = this, я, чесно кажучи, навіть не турбувався про те, щоб застосувати / зателефонувати, але зараз я прочитав про ці методи, дякую за це питання!
Андерс,

Я особисто вважаю, що це не погано саме по собі, але ознака того, що ви могли б трохи оптимізувати свій дизайн.
Дмитро

Відповіді:


50

Як сказали інші: Ця "додаткова змінна" є (на якомусь рівні) єдиним способом дізнатись про те, що thisє спеціальним виразом, і, отже, не є змінною, вона не пов'язана в контексті / закритті виконання.

Однак те, що, на мою думку, ви запитуєте (або що я справді хочу відповісти):

Чи слід ставити var self = thisвгорі кожен метод / конструктор?

Резюме

Хоча я спробував це один раз і мав те саме питання, я більше не використовую цей підхід. Тепер я резервую конструкцію, коли мені потрібен доступ у закритті. Для мене це додає трохи "агов, це те, що я справді хочу!" семантично до мого коду:

this -> this і self -> this (but really that) in a closure

Запитання ала карт:

... Незважаючи на те, що це зазвичай роблять, це здається трохи неправильним. Те, що я сподіваюся знайти в цьому питанні, - кращий спосіб вирішити це питання або щось, що переконає мене, що це цілком добре.

Робіть те, що вам подобається. Не бійтеся спробувати один із методів і повернутися пізніше (але, будь ласка, намагайтеся залишатися послідовними в кожному проекті :-)

Це стандартний спосіб зберігати правильні прив’язки? Чи повинен я стандартизувати використання "self" скрізь, якщо явно не потребую "this".

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

..якщо це вважається трохи злим і чому.

Зло - безглуздий суб’єктивний термін (хоч іноді і весело). Я ніколи не казав, що це зло, просто чому я не дотримуюся підходу. Деякі люди кажуть мені, що я "злий" за те, що не використовую крапки з комою. Я кажу їм, що вони насправді повинні привести вагомі аргументи та / або краще вивчити JavaScript :-)

Мені відомо, що існує також вбудована функція javascript 'apply', щоб чітко визначити область дії під час виклику методу. Чи краще?

Проблема в apply/callтому, що ви повинні використовувати їх у момент виклику функції. Це не допоможе, якщо хтось інший назве один із ваших методів якthis можливо, вже вимкнено. Це найбільш корисно для виконання таких речей, як зворотні виклики у стилі jQuery, де the thisє елементом / елементом зворотного виклику тощо.

Як осторонь ...

Мені подобається уникати "необхідності себе" членам і, таким чином, загалом підвищувати всі функції членів до властивостей, де приймач (this ) просто "протікає", що зазвичай "як очікується".

"Приватні" методи в моєму коді починаються з "_", і якщо користувач їх викликає, це на них. Це також працює краще (обов’язково, насправді) при використанні підходу прототипу до створення об’єкта. Однак Дуглас Крокфорд не погоджується з цим моїм "приватним" підходом, і є деякі випадки, коли пошуковий ланцюг може зірвати вас, вводячи несподіваний приймач:

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

Щасливого кодування.


1
Чудова відповідь. Не могли б Ви докладніше описати це твердження: "Мені подобається уникати" необхідності самоврядування "для членів і, таким чином, загалом підвищувати всі функції членів до властивостей, де приймач (цей) просто" протікає ", що зазвичай" як очікується ". Як ти це робиш?
Еверт

1
@Evert Якщо кожен метод є членом об'єкта, його буде / слід викликати як якусь форму, obj.memberяка зазвичай гарантує thisправильність (оскільки obj->this(obj)->this(obj)->...). Якщо ви побачите посилання на Крокфорд у дописі, ви побачите, що підхід приватного методу порушує шаблон, оскільки приватні «методи» тепер зберігаються у змінних у конструкторі і, отже, не викликаються у obj.memberформі. Це викине thisїх всередину (як thisпросто одержувач функції під час виклику - objу наведених вище прикладах), якщо не буде виконано додаткової роботи.

3
aelfє об'єктом вікна - я б уникав використання зарезервованих слів, а використовував би var that = thisабоvar _self = this
chovy

@chovy Цікавий момент, про який я ніколи не думав. Я думаю, я завжди уникаю проблеми, завжди [крім тих рідкісних випадків, коли я забуваю], використовуючи window.xкваліфікацію в моєму коді (що і я ніколи не використовував window.self..)

Будьте вдвічі злішими. Помістіть var self = falseу глобальний контекст, а потім додайте var self = this;методи, де вам це потрібно. Тоді, якщо колись сумніваєтесь, пишіть (self || this).method(...). Це жахливе кодування, але це добре.
Оруелофіл

17

Так, це стандартний спосіб.

Function.apply() і Function.call() може допомогти, але не завжди.

Розглянемо наступне

function foo()
{
  var self = this;
  this.name = 'foo';

  setTimeout( function()
  {
    alert( "Hi from " + self.name );
  }, 1000 );       
}

new foo();

Якщо ви хотіли це зробити, але уникаєте використання змінної like selfі use call()або apply()замість цього ... ну ... ви подивіться на це і починайте намагатися, але незабаром зрозумієте, що просто не можете. setTimeout()відповідає за виклик лямбда-сигналу, унеможливлюючи використання цих альтернативних стилів виклику. Ви все одно закінчите створювати якусь посередницьку змінну для зберігання посилання на об’єкт.


4
Вибачте, якщо я setTimeout( (function() { alert( "Hi from " + this.name ); }).apply(this), 1000 ); }
неправильно зрозумів

@drodsou Ні, ваш код не працює, подивіться на цей JSBin . Це не вдається із синтаксичною помилкою, оскільки ви не можете просто обернути щось у "()" у JS. Як бачите, window.setTimeout не повертає функцію / об'єкт, а примітив типу "число", що представляє ідентифікатор лічильника.
Sentenza

@Peter Bailey насправді є інший спосіб , однак я не кажу, що це краще рішення.
Sentenza

3
@drodsou Використовуючи apply (this), негайно виконає функцію, таким чином повернувши undefined як перший аргумент setTimeout, однак існує функція прив'язки: window.setTimeout (function () {alert ("Привіт від" + this.name);} .bind (це), 1e3);
AlexanderB

1
Отже ... те, як я бачу це: використання bind дає коротший код, ніж той, що вказаний у цій відповіді :). Отже, ця відповідь неправильна. "Але не завжди" вводить в оману.
Адам Скободзінський

9

Це стандартний спосіб зберігати правильні прив’язки?

Немає стандарту, що стосується JavaScript та систем класів / екземплярів. Вам доведеться вибрати, яку модель об’єкта ви віддаєте перевагу. Ось ще одне посилання на фонове зображення; висновок: висновку немає.

Зазвичай зберігання копії var self= this;(*) у закритті йде рука об руку з об’єктною моделлю, побудованою навколо закриттів з копіями кожного з методів. Це дійсний спосіб робити щось; трохи менш ефективні, але і , як правило , трохи менше роботи , ніж альтернатива, об'єктна модель побудована навколо прототипирования, використовуючи this, apply()і ECMAScript Fifth Edition, bind()щоб отримати пов'язані методи.

Що більше можна вважати «злим», це коли у вас є міш-маш обох стилів в одному коді. На жаль, це робить багато загальноприйнятого коду JS (бо погодьмося, насправді ніхто не розуміє дивний рідний об'єктної моделі в JavaScript).

(*: Зазвичай я використовую thatзамість self; ви можете використовувати будь-яке ім'я змінної, яке вам подобається, але selfвже має дещо незрозуміле і абсолютно безглузде значення як windowчлен, який вказує на саме вікно.)


7

Просто натрапив на це запитання, тому що мої колеги пристрастилися до змінної self / that і я хотів зрозуміти, чому ...

Я думаю, що сьогодні є кращий спосіб вирішити це :

function () {}.bind(this);      // native
_.bind(function () {}, this);   // lodash
$.proxy(function () {}, this);  // jquery

4

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

Це важлива різниця у javascript у порівнянні з багатьма іншими мовами, на яких ми працюємо. Я родом з .Net, тому спочатку такий тип речей здався мені дивним.

Редагувати : Ах, добре, ти все це знаєш. (можливо, все-таки корисно для когось іншого.) Додам, що Apply (і Call) - це більше для використання ззовні, надаючи функції, яку ви викликаєте, певною сферою, про яку ви вже знаєте. Потрапивши у функцію, і ви збираєтеся каскадувати далі до закриття, техніка:

  var self = this;

є найбільш підходящим способом ( простим і зрозумілим ) закріпити поточний обсяг.


2

Швидше за все, це робиться для того, щоб зберегти посилання на те, thisколи область дії має змінитися (у випадку закриття). Не знаю, що я вважав би це поганою практикою чи зразком, ні. Подібні речі ви бачите багато в таких бібліотеках, як jQuery, і багато в роботі з AJAX.


1

Я думаю, є аргумент для того, щоб завжди включати var self = this в будь-який метод: людський фактор.

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

У той же час, я ловлю себе розсіяно, пишучи self.fooз незвички, коли мені не потрібно або додано var self = this. Тому я думаю, що могло б мати сенс просто створити собі звичку завжди включати її, потрібна чи ні.

Біда тільки в тому ... this, selfабо thatвсе потворне віспа на вашому коді , і я частково ненавиджу їх усіх. Тому я думаю, що краще уникати використання делегованих методів, де це можливо, щоб ви могли уникати використання this, thatабо selfпереважну більшість часу, і використовувати, .bind(this)коли ви могли б інакше вдатися до self/that . Дуже рідко, що використання делегатів на прототипі насправді заощадить вам значний обсяг пам’яті.

Приємним побічним ефектом такого підходу є те, що вам не потрібно префіксувати всі ваші приватні змінні _, оскільки вони будуть справді приватними, а загальнодоступні властивості будуть викликані провідними this., роблячи ваш код більш читабельним.

Як сказав бобінс, var that = thisкраще, бо це не тінь window.self. self = thisдля мене звучить менш незручно, але іноді з’являються заплутані повідомлення про помилки, наприклад property myMethod not found on global, тому що ви забули self = thisрядок.


чи не могли б ви надати jsfiddle з ідеєю замінити self на .bind (це), будь ласка?
Sentenza

1
window.setTimeout (function () {this. $ btnAccept.tooltip ('show');} .bind (this), 256);
AlexanderB

1

Я просто хочу зазначити, що "self" еквівалентно "window", спробуйте вивести window === self на консоль. Ви повинні використовувати цей шаблон із назвою "що" або чимось подібним як ім'я змінної, уникайте використання "self", оскільки він уже використовується браузером (одна помилка, і ви створите собі глобальну змінну). Незважаючи на те, що це звучить дивно, краще використовувати `` це '' для його назви, оскільки інші розробники відразу дізнаються, що ви намагалися виконати у своєму коді, уникайте використання нестандартних імен змінних. Я вважаю, що це важлива примітка, але про неї було згадано лише в одному коментарі, тому я хотів зробити її більш помітною.


Спробуйте створити глобальну змінну в браузері за допомогою самооб’єкта та опублікувати скрипку. У моєму браузері (Chromium) це не працює, оскільки воно визначає властивість "self" у "window", якщо ви використовуєте "self = ..." без "var".
Sentenza

Я не впевнений, чому ви створюєте нову глобальну змінну під назвою "self". Звичайно, в JavaScript ви можете змінити сам JavaScript, але при цьому слід бути надзвичайно обережним, оскільки інші розробники очікують "стандартну версію" JavaScript, коли ви надасте їм свій код. Коли ви призначаєте щось самому собі, ви насправді перезаписуєте це, я б уникав цього. Я лише сказав, що коли ви порівнюєте об'єкт вікна за замовчуванням та self, вони за замовчуванням є тим самим об'єктом. Іншими словами, "я" та "вікно" є синонімами
Горан Васич

(одна помилка, і ви створите собі глобальну змінну)
Sentenza

1

Це 6 років потому, і я маю додати кілька речей:

bind()зараз досить поширений, щоб використовувати його скрізь. Я часто використовую його як альтернативу. Іноді відчувається ясніше. Я все ще при нагоді використовую var self = this;. хоча.

Функції стрілок повільно стають життєздатними для використання. Синтаксис трохи коротший, що приємно, але я думаю, що функція вбивства насправді полягає в тому, що за замовчуванням вони завжди прив'язуються до батьківської області.

Це:

var self = this;
var foo = function(a) {

  return self.b + a;

};

Тепер можна записати як:

var foo = a => this.b + a;

Це "найбільш оптимістичне" використання функцій стрілок, але це досить мило.

І на закінчення, у цьому немає нічого поганого:

var self = this;

0

Мені це подобається. Це "я" - пояснювальне. Дуглас Крокфорд має щось сказати з цього приводу. Він заявляє, що використання "того" - це умовність. Ви можете побачити Крокфорд безкоштовно, якщо переїдете до yui-театру і подивитеся відео про Javascript .


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