Javascript var self this

jashmenn / self-eq-this-vs-bind.md

In Javascript this is bound in unexpected ways. Functions, in particular, create a new ‘this’ and so when you want to keep a reference to an «outer» object you sometimes see the pattern:

var self = this; return function(whatever)  return self.foo(whatever); // `this` here is for the inner function >

Is this a good pattern? Shouldn’t we just use .bind(this) on the inner function?

I’ve done some reasearch and here are a few links that discuss this idea along with a little summary of what they suggest.

  • Is var self = this; a bad pattern?
    • Do what feels right
    • Find to do, but could be dangerous because there is a window.self — see also: Window.self
    • Tutorial on binding-sensitive code patterns, using call and apply and why .bind was created
    • This fellow doesn’t care for the idiom, but I don’t find his arguments convincing
    • Explains the self = this pattern but doesn’t really suggest pros and cons
    • Says self = this is fine, but explains how .bind works and why to use it
    • Provides a good list of pros and cons to using .bind
    • Ohhhh I would do anything for scope, but I won’t do that = this — Jake Archibald (@jaffathecake) February 20, 2013
    • @benhowdle $this for jQuery, for plain JS i don’t, use .bind() — Sindre Sorhus (@sindresorhus) February 22, 2013

    To me, it seems that .bind definitely has it’s place. For instance when you have a function in a variable and you want to attach it to a particular this .

    That said, in the case of nested anonymous functions I think that using var self = this; is straightforward and requires less mental accounting vs. using .bind . For example, say we have two nested functions:

    function foo()  return (function()  doSomething((function()  return this.name; // what is `this` again? >).bind(this)); >).bind(this); >
    function foo()  var self = this; return (function()  doSomething((function()  return self.name; // `self` is easy to know because its defined >) >) >

    Of course, because this is a matter of style the issue of «mental accounting» will differ from person to person. But to me, given the semtanics of Javascript’s this , I think that self = this is easy to comprehend when you have nested callbacks.

    Refering to this jsperf : https://jsperf.com/bind-vs-self-closure
    It looks like var self = this; is a lot more faster than the .bind(this) .

    You’re missing a closing parenthesis on the self reference example. 😛

    IMHO, being explicit using bind() is more understandable than a more-cryptic & non-standard context = this , with context often varying among self , that , thisArg , context , ‘ctx’.

    The reassignment as variable doesn’t express a clear intention, because its reason to be lies in an implicit implementation detail (as it often happens in Javascript 😓 ): the closure.
    New JS devs aren’t necessarily aware of closures and about the self = this idiom.
    They might not be aware of what .bind does too; but they can copy-paste & find a more straightforward MDN documentation for it.

    Specifically, .bind ing a function makes it clear:

    • that the intention/verb is to provide a context
    • what is the subjected function
    • what is the context object provided

    In this, bind is unbeatable for clarity and code sharing. Also it is more straightforward to trace, debug & refactor, for developers of any level of experience.
    I tend to prefer it as a habit, even when it’s inconvenient, because it feels cleaner, more structured, and less obscure.

    Only case where I might use var self = this is when I need multiple contexts inside a callback; but in most of my use-cases I tend to resolve that by accessing event.target as second context, and still keep this bound via .bind , without having to get anything from the outer scope.

    function Page() < this.body = document.querySelector('body') this.btn = document.createElement('button') this.btn.textContent = 'click me!' this.btn.addEventListener('click', this.onClick.bind(this)) this.body.append(this.btn) >Page.prototype.onClick = function(event) < var button = event.target var page = this button.textContent = 'clicked!' page.changeBackground() >Page.prototype.changeBackground = function()

    Of course the choice depends also on how the script code is being structured.

    Источник

    Правильный захват контекста в Javascript

    Довольно часто во многих статьях я вижу, как люди захватывают контекст this для использования в анонимной функции и удивляюсь — то, что уже стало стандартом — просто ужасная практика, которая противоречит всем канонам программирования. Вам знакома такая запись?
    var self = this ;
    Может вам тоже стоит переосмыслить этот аспект?

    Итак, пример:

    $( ‘input’ ). bind ( ‘keydown’ , function () var $this = $( this );
    $this . css ( background : $this . val ()
    >);
    >);

    Ненависть

    Я считаю, что в названиях переменных self и $this (а также that, _this, t) кроется зло. Зло кроется по двум причинам.
    Первая — названия совершенно не несут смысловой нагрузки. Таким же чудом мы могли бы использовать, скажем var killmeplz = this ;
    Вторая — иногда контексты могут множиться и пересекаться и тогда настаёт путаница. Например, когда функции у нас вложены одна в другую:

    var self = this ;
    asyncFunc (function () var self2 = this ; // wtf .
    setTimeout (function () self . callMethod ( self2 );
    >, 200 );
    >);

    Решение первое — правильно именуем переменные

    Всегда надо давать вменяемые имена переменным. Это правило относится не к контексту в JS, а к программированию в целом, но во время захвата контекста все об этом забывают. Например:

    $( ‘input’ ). bind ( ‘keydown’ , function () var $colorInput = $( this );
    $colorInput . css ( background : $colorInput . val ()
    >);
    >);

    Это решит проблему со вложенными функциями.

    $block = $( this );
    $( ‘button’ ). each (function () var $button = $( this );
    $. each ( users , function () var user = this ;
    $block . append ( user . init ( $button ));
    >);
    >);

    Но чаще всего такое разнообразие контекстов не требуется. Потому давайте посмотрим на другой способ:

    Принудительное задание контекста функции

    Эту идею я почерпнул из фреймворка MooTools и считаю её замечательной. Немножко расширим прототип Function

    Function. prototype . bind = function ( scope ) var fn = this ;
    return function () return fn . apply ( scope , arguments );
    >;
    >;

    Теперь мы можем не терять контекст в течении всей работы. Если нам нужен только внешний контекст — мы прямо это указываем и код становится значительно прозрачнее:

    Другие возможности работы с .bind

    Часто бывает, что для того, чтобы работать с методом объекта приходится городить очень некрасивую конструкцию. Например (пример на 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
    >
    >);

    мы не можем передать в метод get просто ссылку на метод parse:

    потому что тогда потеряется контекст метода. метод bind поможет нам в этом и мы видим, насколько яснее стал этот код:

    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
    >
    >);

    Небольшой кусок кода из карточной игры Bridge на LibCanvas, демонстрирующий использование bind.
    Суть ассинхронности в том, что ИИ не должен совершать никаких действий, пока летит карта.
    Например, он берет карту из колоды, но может её туда положить только после того, как карта долетит, иначе будет неприятный для игрока эффект.

    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 ();
    >
    // ..
    >);

    Источник

    Читайте также:  Css disable background color
Оцените статью