Proper javascript context capture

    Quite often, in many articles, I see people grab this context for use in an anonymous function and am surprised that what has already become the standard is just a terrible practice that contradicts all the canons of programming. Do you know such a record?
    var self = this;
    Maybe you should also rethink this aspect?

    So an example:
    var self = this;
    asyncFunc(function () {
        self.callMethod();
    });


    Another example (jQuery):
    $('input').bind('keydown', function () {
        var $this = $(this);
        $this.css({
            background : $this.val()
        });
    });


    What do you say about them?

    Hatred


    I believe that the names of the variables self and $ this (as well as that, _this, t) are evil. Evil lies for two reasons.
    First, the names do not carry a semantic load at all. In the same miracle, we could use, say, the var killmeplz = this;
    Second - sometimes contexts can multiply and intersect, and then confusion arises. For example, when functions are nested inside one another:
    var self = this;
    asyncFunc(function () {
        var self2 = this; // wtf ?!!
        setTimeout(function () {
            self.callMethod(self2);
        }, 200);
    });


    Solution one - name variables correctly


    You should always give sane names to variables. This rule does not apply to context in JS, but to programming as a whole, but during context capture everyone forgets about it. For instance:
    $('input').bind('keydown', function () {
        var $colorInput = $(this);
        $colorInput.css({
            background : $colorInput.val()
        });
    });


    This will solve the problem with nested functions.
    $block = $(this);
    $('button').each(function () {
        var $button = $(this);
        $.each(users, function () {
            var user = this;
            $block.append(user.init($button));
        });
    });


    But more often than not, such a variety of contexts is required. So let's look at another way:

    Force function context


    I got this idea from the MooTools framework and I think it's great. Extend Function prototype a bit
    Function.prototype.bind = function (scope) {
        var fn = this;
        return function () {
            return fn.apply(scope, arguments);
        };
    };


    Now we can not lose context throughout the work. If we need only an external context, we directly indicate this and the code becomes much more transparent:
    asyncFunc(function () {
        this.callMethod();
    }.bind(this));


    Other options for working with .bind


    It often happens that in order to work with the method of the object you have to fence a very ugly design. For example (example on MooTools):
    var Analizer = new Class({
        initialize : function (name) {
            this.dataRouter = new DataRouter[name]();
        },
        start : function () {
            var analizer   = this;
            this.dataRouter.get(function (data) {
                analizer.parse(data);
            });
        },
        parse : function (data) {
            // parsing data, using this.privateMethods
        }
    });


    we cannot pass to the get method just a reference to the parse method:
    dataGetter.get(analizer.parse);


    because then the context of the method is lost. The bind method will help us with this and we see how much clearer this code has become:
    var Analizer = new Class({
        initialize : function (name) {
            this.dataRouter = new DataRouter[name]();
        },
        start : function () {
            this.dataRouter.get(
                this.parse.bind(this)
            );
        },
        parse : function (data) {
            // parsing data, using this.privateMethods
        }
    });


    A small piece of code from the Bridge card game on LibCanvas demonstrating the use of bind.
    The essence of asynchrony is that the AI ​​should not take any action while the map is flying.
    For example, he takes a card from the deck, but can put it there only after the card reaches, otherwise the effect will be unpleasant for the player.
    Bridge.AI = new Class({
        // ..
        putCardSmart : function (card) {
            this.putCard( card,
                // Этот метод вызовется только когда карта долетит, но он сохранит контекст.
                this.finishSmart.bind(this)
            );
        },
        getCardSmart : function () {
            this.getCard(function (card) {
                this.canPutCard(card) ?
                    this.putCardSmart(card) :
                    this.finishSmart();
            }.bind(this)); // Мы захватываем контекст.
        },
        finishSmart : function () {
            this.canFinishMove() ?
                this.finishMove() :
                this.movement();
        }
        // ..
    });


    Bad Tips Examples


    vl.vg/28.01.2010/tooltip-jquery
    blog.kron0s.com/javascript-programming-patterns_2
    habrahabr.ru/blogs/jquery/52185

    Also popular now: