Backbone и bindAll: «функция не определена» - PullRequest
4 голосов
/ 07 июля 2011

У меня проблемы с использованием bindAll.Я получаю ошибку func is undefined.Любые мысли о том, что я делаю не так?

Я пробовал и

  • bindAll (не удается с вышеуказанной ошибкой) и
  • индивидуальный bind s (не работает)
window.test = Backbone.View.extend({

  collection: null

  initialize: ->
    console.log('initialize()')
    console.log(this)
    # _.bindAll(this, ["render", "foo"])
    _.bind(this.render, this) # these don't throw errors, but binding won't work
    _.bind(this.foo, this) 
    @collection = new Backbone.Collection()
    @collection.bind "add",     @render
    @collection.bind "add",     @foo
    @render()

  foo: ->
    # won't be bound to the view when called from the collection
    console.log("foo()")
    console.log(this)
    console.log(this.collection) # undefined (since this is the collection, not the view)

  render: ->
    console.log("render()")
    console.log(this)
    return this

})
testInstance = new window.test();
# using _.bind instead of bindAll we get past this point, however this won't be the view
testInstance.collection.add({})

Ответы [ 3 ]

11 голосов
/ 07 июля 2011

Вы используете CoffeeScript, вам не нужно подчеркивание.В CoffeeScript он встроен. Просто используйте «жирные стрелки»

foo: =>
    #Your foo implementation goes here
render: =>
    # your render implementation goes here

Найдите «жирную стрелку» здесь: http://jashkenas.github.com/coffee-script/

Однако, что касается _.bindAll, вы неНеобходимо предоставить имена методов в виде массива.Вы можете сделать либо _.bindAll(this), либо _.bindAll(this, 'render', 'foo') (имена методов - это var args, а не явный список).Посмотрите, поможет ли это.

8 голосов
/ 07 июля 2011

Питер Лайонс прав в обоих случаях. Вы хотели бы передать каждую функцию в качестве аргумента bindAll, а не передавать массив функций. А при использовании coffeescript жирная стрелка - это отличный способ привязать функцию к контексту, в котором она была определена.

Я хотел ответить , почему _.bind не работает для вас (потому что мне потребовалось много времени, чтобы разобраться). Ответ в том, что _.bind не изменяет передаваемую вами функцию, а создает новую функцию с предоставленными аргументами. Это было бы более соответствующим образом помечено createBoundFunction. Таким образом, заставить _.bind работать в вашем примере будет просто:

this.render = _.bind(this.render, this)
this.foo = _.bind(this.foo, this) 

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

var __bind = function(fn, me){ return function(){return fn.apply(me, arguments); }; }

CoffeeScript вставляет вышеуказанную функцию в каждый файл, который использует жирную стрелку (=>). Это старый способ. Он создает и возвращает новую функцию, которая вызывает вашу функцию и применяет передаваемый вами контекст и аргументы. Затем CoffeeScript генерирует конструктор и вызывает __bind для каждой определенной функции жирной стрелки. Для решения Питера Лиона сгенерированный код должен выглядеть так:

this.render = __bind(this.render, this)
this.foo = __bind(this.foo, this) 

В моем текущем проекте у меня 9 представлений, в которых используется жирная стрелка, поэтому я определил __bind 9 раз, что, по-видимому, нарушает DRY, но кого это волнует, оно генерируется для меня.

ECMAScript5 запрещает новый метод в прототипе функции.

 Function.prototype.bind(thisArg [, arg1[, arg2[, ...]]])

Этот метод уменьшит ваш код до следующего вида:

this.render = this.render.bind(this)
this.foo = this.foo.bind(this)

без внешней библиотеки или сгенерированного метода. Суть в том, что это поддерживается только в последних + лучших браузерах (FF4, IE9, CH10>), поэтому использование его исключительно будет невозможно в течение нескольких лет.

Подчеркивание объединяет эти два понятия:

_.bind = function(func, obj) {
  if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func,   slice.call(arguments, 1));
  var args = slice.call(arguments, 2);
  return function() {
    return func.apply(obj, args.concat(slice.call(arguments)));
  };
};

Где nativeBind равен Function.prototype.bind. Подчеркните, проверяет наличие нового метода связывания ECMA5 и, если его нет, создает анонимную функцию, которая вызывает вашу функцию в выбранном вами контексте.

Если бы я знал или мог найти информацию о некоторых преимуществах, которые предоставляет Function.prototype.bind, я бы сказал, что нужно избегать жирной стрелки CS и использовать _.bind, особенно для магистральных проектов, где вы подчеркнули, что уже включены в ваши библиотеки , но я не знаю, что это дает преимущество, поэтому, вероятно, это не имеет значения, так или иначе.

1 голос
/ 20 декабря 2011

Backbone теперь позволяет добавить дополнительный параметр для привязки this к обратному вызову. См. http://documentcloud.github.com/backbone/#FAQ-this. Таким образом, метод вашего представления будет привязан к представлению, а не к коллекции.

...