Как использовать функцию Function.prototype.bind в JavaScript.

Задача привязки функции к объекту в JavaScript, скорее всего, не вызовет у вас практически никаких трудностей. Но когда вам нужно решить проблему использования контекста объекта this внутри другой функции, начинаются настоящие сложности. Вы не можете однозначно определить, что же необходимо делать, и вот тут на помощь приходит подход с использованием Function.prototype.bind().

Результатом первоначального анализа проблемы, вероятнее всего, станет попытка идентифицировать this, как переменную, на которую можно ссылаться при изменении контекста. Многие люди выбирают в качестве имени переменной обозначения типа self, _this, а иногда и context. В принципе, все эти варианты верны и ничего плохого не случится. Однако существует более правильный подход.

Джек Арчибальд пишет в Твиттере о кэшировании объекта this:

Я готов сделать все что угодно для нашей сферы деятельности, но я не буду использовать такую операцию, как that = this

— Джек Арчибальд (Jake Archibald) (@jaffathecake) 20 февраля 2013 года

Некоторые вещи стали для меня более очевидными, когда Сайндер Сорхус дал четкое определение:

@benhowdle $ this для jQuery, а для простого JS я не использую .bind()

— Сайндер Сорхус (Sindre Sorhus) (@ sindresorhus) 22 февраля 2013 года

В течение многих месяцев я игнорировал этот мудрый совет.

Какую же проблему мы пытаемся решить?

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

var myObj = {

    specialFunction: function () {

    },

    anotherSpecialFunction: function () {

    },

    getAsyncData: function (cb) {
        cb();
    },

    render: function () {
        var that = this;
        this.getAsyncData(function () {
            that.specialFunction();
            that.anotherSpecialFunction();
        });
    }
};

myObj.render();

Если бы мы оставили структуру вызова функции как this.specialFunction(), то было бы получено следующее сообщение об ошибке:

Uncaught TypeError: Object [object global] has no method 'specialFunction'

Мы должны обеспечить правильную работу объекта myObj при инициировании функции обратного вызова. Вызов that.specialFunction() позволяет предположить о правильности выполнения нашей функции. Тем не менее, весь этот программный код мог бы быть несколько упрощен с помощью Function.prototype.bind().

Давайте перепишем наш пример:

render: function () {

    this.getAsyncData(function () {

        this.specialFunction();

        this.anotherSpecialFunction();

    }.bind(this));

}

Что мы только что сделали?

Итак, .bind() просто создает новую функцию, которая при вызове устанавливает указанное значение для ключевого слова this. Мы передаем наш желаемый контекст в функцию .bind(). Затем, когда происходит инициирование функции обратного вызова, this ссылается на myObj.

Если вы заинтересованы в том, как выглядит синтаксис функции Function.prototype.bind(), то можете рассмотреть следующий, достаточно простой пример:

Function.prototype.bind = function (scope) {
    var fn = this;
    return function () {
        return fn.apply(scope);
    };
}

А вот очень простой вариант использования:

var foo = {
    x: 3
}

var bar = function(){
    console.log(this.x);
}

bar(); // undefined

var boundFunc = bar.bind(foo);

boundFunc(); // 3

Мы создали новую функцию, которая, при выполнении устанавливает this в foo. Обратите внимание, что это не глобальная область, как в том примере, где мы вызывали функцию bar();.

поддержка браузерами.

Браузер Какая версия поддерживает
Chrome 7
Firefox (Gecko) 4,0 (2)
Internet Explorer 9
Opera 11.60
Safari 5.1.4

Как вы наверное уже успели заметить, к сожалению, Function.prototype.bind не поддерживается в Internet Explorer 8 и более ранних версиях. Таким образом вы столкнетесь с рядом проблем, если попытаетесь воспользоваться этой функцией без обратного вызов.

К счастью, такой прекрасный ресурс, как Mozilla Developer Network предоставляет надежную альтернативу для тех случаев, когда браузер не поддерживает родной .bind() метод:

if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}

var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();

ret ...

Если вы хотите прочитать полностью статью, посетите сайт наших спонсоров

Comments are closed.