Подтвердить что ты не робот

Function.prototype.bind

У меня довольно интересный вопрос о реализации функции EcmaScript-5 Function.prototype.bind. Обычно, когда вы используете bind, вы делаете это следующим образом:

var myFunction = function() {
    alert(this);
}.bind(123);

// will alert 123
myFunction();

Хорошо, так что круто, но что, возможно, произойдет, когда мы это сделаем?

// rebind binded function
myFunction = myFunction.bind('foobar');
// will alert... 123!
myFunction();

Я понимаю, что это полностью логичное поведение с точки зрения реализации функции Function.prototype.bind(https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind). Но в реальных условиях это совершенно бесполезное поведение, не так ли? Вопрос в том, что это ошибка или функция? Если это ошибка, почему она нигде не упоминается? Если это особенность, почему Google Chrome с собственной реализацией "bind" ведет себя абсолютно одинаково?

Чтобы сделать это более понятным, что, на мой взгляд, будет иметь больше смысла, вот фрагмент кода, который реализует Function.prototype.bind несколько иначе:

if (!Function.prototype.bind) {
    Function.prototype.bind = function() {
        var funcObj = this;
        var original = funcObj;
        var extraArgs = Array.prototype.slice.call(arguments);
        var thisObj = extraArgs.shift();
        var func = function() {
            var thatObj = thisObj;
            return original.apply(thatObj, extraArgs.concat(
                Array.prototype.slice.call(
                    arguments, extraArgs.length
                )
            ));
        };
        func.bind = function() {
            var args = Array.prototype.slice.call(arguments);
            return Function.prototype.bind.apply(funcObj, args);
        }
        return func;
    };
}

Итак, попробуйте следующее:

// rebind binded function
myFunction = myFunction.bind('foobar');
// will alert... "foobar"
myFunction();

По-моему, замена "this" имеет смысл...

Так что вы, ребята, думаете об этом?

4b9b3361

Ответ 1

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

Ты не единственный, кого я слышал, кто думал, что это странно. Крис Лири, который работает над движком Mozilla JS (как и я), подумал, что это немного странно, подняв вопрос в Twitter пару месяцев назад. И в несколько иной форме я помню, как один из хакеров Mozilla Labs спрашивал, есть ли способ "отвязать" функцию, чтобы извлечь из нее целевую функцию. (Если бы вы могли это сделать, вы могли бы связать его с другим this, по крайней мере, если бы вы также могли извлечь список связанных аргументов, чтобы также передать его.)

Я не помню, как обсуждалась проблема, когда был указан bind. Тем не менее, я не обращал особо пристального внимания на список рассылки es-discuss в то время, когда этот материал был хэширован. Тем не менее, я не верю, что ES5 искал новшества в этом районе, просто "проложил корову", чтобы заимствовать фразу.

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

Ответ 2

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

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

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

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

Таким образом, нет никакой тайны в том, почему она была стандартизирована или изобретена так, как она была: bind возвращает функцию, которая предоставляет новое это и добавляет аргументы, и работает одинаково для всех функций. Простейшая возможная семантика.

Только так он действительно безопасен в использовании - если привязка будет иметь значение между уже связанными функциями и "нормальными", нужно было бы протестировать до привязки. Хорошим примером будет jQuery.each, который передает это новое. Если bind связывает особые функции, не будет безопасного способа передать связанную функцию в jQuery.each, так как это будет перезаписано для каждого вызова. Чтобы исправить это, jQuery.each каким-то образом пришлось бы связать связанные функции.