Магистраль: правильный способ передачи 'this' ссылки на анонные функции при обратных вызовах успеха/ошибки

С учетом функции просмотра позвоночника ниже, каков правильный способ передачи this (т.е. текущего представления) анонимной функции, определенной в обратных вызовах?

addSomething: function(e) {
    var newSomething= this.model.somethings.create({
        someProperty: xxx
    }, {
        success: function(m, response) {
            this.doSomething(); //***HERE****
        },
        error: function(m, response) {
            //Error
        }
    });
},

Без изменений и изменений, this в функции anon устанавливается в окно.

Я могу установить ссылку как это:

var thisView = this;

а затем просто обратитесь к thisView вместо this в функцию anon, но это не выглядит очень элегантным. Есть ли лучший способ?

Ответы

Ответ 1

Чтобы лучше организовать мой код и решить эту проблему, я никогда не ставил успешные и обратные вызовы ошибок непосредственно в соответствии с моими вызовами. Я всегда разделял их на свои собственные функции. Это позволило мне сохранить код чистым, а также использовать метод _.bindAll, чтобы убедиться, что у меня есть правильный контекст для this.

SomeView = Backbone.View.extend({
  initialize: function(){
    _.bindAll(this, "createSuccess", "createError");
  },

  addSomething: function(e) {
    var newSomething= this.model.somethings.create({someProperty: xxx}, {
      success: this.createSuccess,
      error: this.createError
    });
  },

  createSuccess: function(m, response) {
    this.doSomething();
  },

  createError: function(m, response) {
    //Error
  }
});

Ответ 2

Вы можете использовать call:

this.doSomething.call(this);

Или отправьте все, что хотите this, в doSomething

Ответ 3

Вы также можете использовать соглашение that:

addSomething: function(e) {

var that = this;

var newSomething= this.model.somethings.create({
        someProperty: xxx
    }, {
        success: function(m, response) {
            that.doSomething(); //***THAT HERE****
        },
        error: function(m, response) {
            //Error
        }
    });
}

Это довольно распространенный шаблон в javascript, особенно когда контекст теряется с вложенными функциями. Вложенные функции имеют доступ к переменным во внешней функции и, следовательно, работают.

Подход Derick делает код более чистым, независимо от факта наличия дополнительных функций, но если вы не хотите, чтобы они использовали "это";)

Ответ 4

После того, как вы заболели, увидев слишком много "var that = this"; Я начал передавать атрибут "вид" в параметры вызова.

this.model.destroy({success:
    function (model, resp, options) {
        model.releaseLock(false);
        window.location.href = options.view.getURLForModelID(model.get('id'));
    },
    error: function (model, resp, options) {
        options.view.displayError(resp.message);
    },
    view: this
});

изменить

Другим вариантом, к которому я привык, является использование _.bind() для передачи текущего контекста.

this.model.destroy({success:
    function (model, resp, options) {
        model.releaseLock(false);
        window.location.href = options.view.getURLForModelID(model.get('id'));
    },
    error: _.bind(function (model, resp, options) {
        this.displayError(resp.message);
    }, this),
});

Ответ 5

Вы можете передать контекст, не используя call, bind или apply (или Lo-Dash), создав анонимную функцию, передав this, а затем сразу же вызовите методы обработчика на прототипе объекта как показано в следующем примере CoffeeScript:

View = require('views/base/view')

module.exports = class LoginPageView extends View
  template: require('./templates/login-page')
  events:
    'submit form': '_doLogin'

  # Called when the template is bound using Chaplin
  attach: ->
    super
    @$loginButton = @$('[name="login"]')

  _doLogin: (evt) ->
    evt.preventDefault()
    @$loginButton.attr 'disabled', true
    email = @$('[name="email"]').val()
    password = @$('[name="password"]').val()
    @model.save {email, password},
      success: (model, response, options) =>
        @_loginSuccess model, response, options
      error: (model, response, options) =>
        @_loginError model, response, options

  _loginSuccess: (model, response, options) ->
    # TODO: Implement method body

  _loginError: (model, response, options) ->
    # TODO: Implement method body

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

Ответ 6

Вы также можете использовать $. proxy:

        success: $.proxy(function(m, response) {
            this.doSomething(); 
        }, this),
        error: function(m, response) {
            //Error
        }