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
  })
}

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


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
  }
}

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 возвращает новую функцию, которая вызывает оригинальный метод с указанной вами областью действия.


Аналогично: $('#foobar').ready('click', this.doSomething.call(this));верно?
Muers

bindМетод не работает в старых браузерах, поэтому используйте $.proxyметод.
Guffa


1

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

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

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

});
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.