2015-01-25 5 views
0

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

Зачем использовать эту идиому, а не, например, использовать методы на прототипе напрямую?

Получает ли это некоторые преимущества с точки зрения потребления памяти/возможность бесплатного использования памяти, когда события не связаны с событиями DOM?

function F() { 
    var onFoo = this._onFoo.bind(this); // Why? 

    document.getElementById('foo').onClick(onFoo); 
} 

F.prototype._onFoo = function() { /*...*/ } 
+1

Это должно объяснить это: [Как получить доступ к правильному 'this'/context внутри обратного вызова?] (Http://stackoverflow.com/a/20279485/218196) –

+0

Но я не думаю, что это причина для идиомы в этом случае. Я знаю, что делает связка. Возможно, я должен был сформулировать вопрос, привязывает ли связанная копия функции к обратному вызову DOM какие-либо преимущества, помимо привязки к получателю? – Ben

+0

Дополнительно к Felix: '.bind' - это дешевый способ создания частично прикладных функций в JS – zerkms

ответ

1

Вопрос заключается в том, что обработчики событий устанавливают свои собственные значения для this при вызове функции обратного вызова. Это значение, как правило, связано с обработчиком события, а не с объектом, к которому привязан метод. Например, в вашем примере:

document.getElementById('foo').onClick(myObj.myFunc); 

this указатель в myFunc будет установлен на элемент DOM, который имел обработчик события (в данном случае, foo элемент). Но это не myObj, поэтому myFunc в этом случае не смог получить доступ к каким-либо своим собственным переменным экземпляра с помощью указателя this (обычный способ доступа методов к данным экземпляра).

Итак, если у вас есть метод, который хочет получить доступ к собственным данным экземпляра, когда он вызывается непосредственно обработчиком событий, вам нужно сделать что-то другое, кроме как просто передать метод обработчику событий. Есть пара способов обойти эту проблему.

Один из способов сделать это так использовать .bind(), который возвращает новую функцию заглушки кто есть функция, чтобы установить this перед вызовом функции, как это:

document.getElementById('foo').addEventListener('click', myObj.myFunc.bind(myObj)); 

В этом случае .bind() фактически возвращает новую функцию заглушки кто есть функция должна установить значение this до myObj до того, как оно называет myFunc.

Вы также можете сделать это вручную, сами, как это:

document.getElementById('foo').addEventListener('click', function(e) { 
    myObj.myFunc(); 
}); 

Но, как вы можете видеть, .bind() обеспечивает ярлык, который занимает меньше кода (именно поэтому он был изобретен).


Потенциальным недостатком использования .bind() в некоторых случаях является то, что вы больше не имеют доступа к значению this, что вызывающий обратного вызова бы установить себя, потому что .bind() бросил это значение прочь и заменить его вместе с своя. В приведенном выше примере обработчика событий это не проблема, потому что исходный источник события может быть доступен через аргумент e, который передается обработчику события, чтобы он не был потерян, если вам это нужно.


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


Оказывается, что одна вещь, которая сбивает с толку вас в том, что объекты в JavaScript, назначаются или передаются по указателю (некоторые называют его по ссылке, но имеет некоторые коннотации, которые не применяются здесь, так что я буду использовать фраза по указателю).

var x = {}; 
x.myFunc = function() {console.log("hello");}; 
x.myFunc();  // generates "hello" in the console 

var t = x.myFunc; // save reference to the function that x.myFunc currently points to 
delete x.myFunc; // remove property myfunc from the x object 
t();    // generates "hello" in the console 

t() все еще работает после того, как событие x.myFunc был удален, потому что оба т и x.myFunc имели ссылку (или указатель) к одной и той же функции. Выполнение delete x.myFunc просто удалило свойство myFunc из объекта x. Функция, которую указывают x.myFunc, будет только «освобождена» GC, если нет других ссылок на нее. Но есть другая ссылка на эту функцию в t, поэтому она не освобождена, и t() может использовать ее до тех пор, пока существует t.

+0

Просьба прокомментировать, может ли использование bind создавать экземпляры функций в прототипе дает какую-либо выгоду/дополнительный контроль за использованием памяти/сборкой мусора функций? – Ben

+0

@Ben - что такое «использование bind для создания экземпляров функций на прототипе». Я не понимаю, что означает эта фраза. Ваш код иллюстрирует использование '.bind()' в конструкторе для создания временной функции-заглушки, которая всегда использует этот объект при его вызове. Эта новая «связанная» функция - это локальная переменная, используемая только внутри конструктора. – jfriend00

+0

'bind' будет * копировать * функцию, устанавливающую ресивер (и частично применяя args). Является ли эта семантика копии используемой для более тонкого контроля над распределением/освобождением памяти? Например, я мог бы привязывать функции непосредственно к каждому экземпляру F, но для этого потребовалось бы больше памяти, потому что в каждой жизни для каждого из этих экземпляров был бы экземпляр каждой функции обратного вызова * для каждого экземпляра F. Но если используется связанная копия функции на прототипе, то, если я хочу развязать функцию, тогда эта связанная копия обратного вызова может быть собрана в мусор. Имеет ли это смысл? – Ben

Смежные вопросы