var self = this?

182

Использование методов экземпляра в качестве обратных вызовов для обработчиков событий изменяет область действия thisс «Мой экземпляр» на «Независимо от того, что только что вызвало обратный вызов» . Так что мой код выглядит так

function MyObject() {
  this.doSomething = function() {
    ...
  }

  var self = this
  $('#foobar').bind('click', function(){
    self.doSomethng()
    // this.doSomething() would not work here
  })
}

Это работает, но это лучший способ сделать это? Это выглядит странно для меня.

defnull
источник
15
Вам следует избегать, selfпоскольку window.selfобъект существует, и вы можете случайно использовать его, если забудете объявить свой собственный selfvar (например, при перемещении некоторого кода). Это может раздражать, чтобы обнаружить / отладить. Лучше использовать что-то вроде _this.
Alastair Maw
3
Из руководства по JavaScript : «Значение thisJavaScript является динамическим в JavaScript. Оно определяется при вызове функции , а не при ее объявлении».
DavidRR
Дело в глобальном масштабе self === this. Поэтому selfв локальном контексте имеет смысл и следует шаблону.
programmer5000

Ответы:

210

Этот вопрос относится не только к jQuery, но и к JavaScript в целом. Основная проблема заключается в том, как «направить» переменную во встроенные функции. Это пример:

var abc = 1; // we want to use this variable in embedded functions

function xyz(){
  console.log(abc); // it is available here!
  function qwe(){
    console.log(abc); // it is available here too!
  }
  ...
};

Этот метод основан на использовании замыкания. Но это не работает, thisпотому что thisэто псевдопеременная, которая может динамически меняться от области действия к области видимости:

// we want to use "this" variable in embedded functions

function xyz(){
  // "this" is different here!
  console.log(this); // not what we wanted!
  function qwe(){
    // "this" is different here too!
    console.log(this); // not what we wanted!
  }
  ...
};

Что мы можем сделать? Присвойте его какой-нибудь переменной и используйте через псевдоним:

var abc = this; // we want to use this variable in embedded functions

function xyz(){
  // "this" is different here! --- but we don't care!
  console.log(abc); // now it is the right object!
  function qwe(){
    // "this" is different here too! --- but we don't care!
    console.log(abc); // it is the right object here too!
  }
  ...
};

thisне является уникальным в этом отношении: argumentsэто другая псевдопеременная, которая должна обрабатываться таким же образом - с помощью псевдонимов.

Евгений Лазуткин
источник
7
Вы действительно должны привязать «this» к вызову функции, а не передавать его или обходить переменные области видимости
michael
10
@ Майкл, почему это?
Крис Андерсон
@michael, вы можете работать внутри pipe, map, подписки ... в одной функции и не вызывать функции, в этом случае меняется "this", поэтому решением является область видимости переменной
Skander Jenhani
@SkanderJenhani, этому комментарию 8 лет. Мой ответ был бы совсем другим сегодня
Майкл
18

Да, это похоже на общий стандарт. Некоторые кодеры используют себя, другие используют меня. Он используется как ссылка на «реальный» объект, а не на событие.

Это то, что мне потребовалось немного времени, чтобы действительно получить, сначала это выглядит странно.

Я обычно делаю это прямо в верхней части моего объекта (извините за мой демонстрационный код - он более концептуален, чем что-либо еще и не является уроком по отличной технике кодирования):

function MyObject(){
  var me = this;

  //Events
  Click = onClick; //Allows user to override onClick event with their own

  //Event Handlers
  onClick = function(args){
    me.MyProperty = args; //Reference me, referencing this refers to onClick
    ...
    //Do other stuff
  }
}
BenAlabaster
источник
12
var functionX = function() {
  var self = this;
  var functionY = function(y) {
    // If we call "this" in here, we get a reference to functionY,
    // but if we call "self" (defined earlier), we get a reference to function X.
  }
}

edit: несмотря на то, что вложенные функции внутри объекта принимают глобальный объект окна, а не окружающий объект.

Серкан
источник
Вы, кажется, описываете, что var self = thisделает, но это не то, что задает вопрос (вопрос в том, является ли это лучшим решением проблемы).
Квентин
3
«Основная проблема заключается в том, как« направить »переменную во встроенные функции». Я согласен с этим утверждением в принятом ответе. Кроме того, нет ничего плохого в «этой» практике. Это довольно часто. Итак, в ответ на часть «код выглядит странно для меня», я решил объяснить, как он работает. Я полагаю, что причина того, что «код выглядит странно для меня», происходит из-за путаницы в том, как он работает.
серкан
12

Если вы используете ES2015 или скрипт типа и ES5, то вы можете использовать функции стрелок в своем коде, и вы не столкнетесь с этой ошибкой, и это относится к желаемой области действия в вашем экземпляре.

this.name = 'test'
myObject.doSomething(data => {
  console.log(this.name)  // this should print out 'test'
});
Аран Декар
источник
5
Как объяснение: в ES2015 функции стрелок захватывают thisих определяющую область. Обычные определения функций этого не делают.
defnull
@defnull Не думаю, что это что-то особенное. Функции создают область блока. поэтому использование стрелочной нотации вместо функции удерживает вас от создания области видимости, поэтому она все равно будет ссылаться на внешнюю переменную this.
Нимешка Шрималь
4

Одним из решений этой проблемы является привязка всего вашего обратного вызова к вашему объекту с помощью bindметода javascript .

Вы можете сделать это с помощью именованного метода,

function MyNamedMethod() {
  // You can now call methods on "this" here 
}

doCallBack(MyNamedMethod.bind(this)); 

Или с анонимным обратным вызовом

doCallBack(function () {
  // You can now call methods on "this" here
}.bind(this));

Делая это вместо того, чтобы прибегать к var self = thisдемонстрациям, вы понимаете, как привязка thisведет себя в javascript и не зависит от ссылки на закрытие.

Кроме того, оператор жирной стрелки в ES6 - это то же самое, что вызов .bind(this)анонимной функции:

doCallback( () => {
  // You can reference "this" here now
});
Барт Дорси
источник
Лично я думаю, что .bind (this) просто больше печатает, изменяя ссылку this для меня (или чего-то еще), а затем просто используя ее вместо этого для более чистого кода. Жирная стрела - это будущее.
Давидбуттар
3

Я не использовал jQuery, но в такой библиотеке, как Prototype, вы можете привязать функции к определенной области. Итак, с учетом этого ваш код будет выглядеть так:

 $('#foobar').ready('click', this.doSomething.bind(this));

Метод bind возвращает новую функцию, которая вызывает оригинальный метод с указанной вами областью действия.

neonski
источник
Аналогично: $('#foobar').ready('click', this.doSomething.call(this));верно?
Muers
bindМетод не работает в старых браузерах, поэтому используйте $.proxyметод.
Guffa
2

Просто добавьте к этому, что в ES6 из-за функций стрелок вам не нужно это делать, потому что они фиксируют thisзначение.

август
источник
1

Я думаю, что это на самом деле зависит от того, что вы собираетесь делать внутри своей doSomethingфункции. Если вы собираетесь получить доступ к MyObjectсвойствам, используя это ключевое слово, то вы должны использовать это. Но я думаю, что следующий фрагмент кода также будет работать, если вы не делаете никаких специальных вещей, используя object(MyObject)свойства.

function doSomething(){
  .........
}

$("#foobar").ready('click', function(){

});
Скотт Харрисон
источник