Hacker News new | past | comments | ask | show | jobs | submit login
Replacing ‘that = this’ assignments in JavaScript with Arrow Functions (dbwriteups.wordpress.com)
57 points by kiyanwang on April 14, 2017 | hide | past | favorite | 44 comments



I have never subscribed to this particular pattern (antipattern IMO), though I do agree with a lot of the concepts Crockford proposes in "The Good Parts." The concept of 'this' is complicated enough in ES5, with a few new possibilities for its value in ES6+.

Since this is nearly always the pattern that's followed in the class pattern, I nearly always alias `this`, if needed, to the lowercase instance name or some short identifier that semantically maps to "an instance of the class". It gets you instantly out of both the problem of "I already used `that` as an identifier, now what?" as well as providing some nice context to what you're working on if you happen to be scrolled beyond the function/constructor name.

    function SomeClass(arg1, secretArg) {
      var someClass = this; // or `var sc = this`, more often these days

      // Hopefully do something more interesting than this...
      someClass.arg1 = arg1;
    
      someClass.method = function(foo) {
        someClass.bar = foo;
        // Do something with the "private" param via closure.
        secretArg.count++;
      };
    }
Even if lambda expressions do fix the issue of "which that is that" it doesn't provide me any helpful context for when I'm deep in coding or reading modes.


Unfortunately, there are several problems with this approach:

1) You're creating a new function instance for `SomeClass.method` every single time you create an instance of SomeClass. This will slow down construction.

2) `method` is an instance property, not a prototype property, and so takes up more memory for a reference per instance, plus the memory of the function instance.

3) In that function instance, you're closing over the references to `someClass` and `arg1` even though they're already available in `method` as `this` and `this.arg1`, also increasing the memory of instances.

4) Setting method will cause an unnecessary hidden-class transition.

If you really want to hide `secretArg`, use a WeakMap:

    const secrets = new WeakMap();
    class SomeClass {

      constructor(arg1, secretArg) {
        this.arg1 = arg1;
        this.bar = undefined;
        secrets.set(this, secretArg);
      }

      method(foo) {
        this.bar = foo;
        const secret = secrets.get(this);
        secret.count++;
      }
    }
This will result in much lighter weight instances.


I understand there are some runtime implications but the vast majority of the "classes" created in my applications are instantiated once or only at startup. The memory usage and speed implications are negligible compared to the added maintainability.

In the grand scheme of all the allocation and computation taking place in my applications, these concerns are very far down the line.

Now, if you happen to be creating an application or framework that for some reason has to do a whole lot of allocating and might be meaningfully and measurably affected by such issues, then by all means use prototype "methods" when it will make a difference.

Personally, code clarity is much more important than making sure my application starts microseconds faster and allocates several bytes fewer per instance when there are only going to be ~10s of instances ever created.


As long as we are extremely clear of the implications it's a good enough reasoning.

Many people, especially newcomers, believe that just because their code initializes a class only N times it's being instanced exactly N times. This is false more often than not, especially in server environments.


> code clarity

This makes sense if it's only you working on a project, but it would be pretty confusing and weird for someone new to join.


What would be confusing and weird?


It being done nowhere else? Muscle memory? It looking non-standard and harder to parse?


I'd argue that adhering workaround for things that have language-level solutions nowadays hurts code clarity, if other people have to work with your code. Sure, the non-functional noise fades to the background when you're used to the pattern, but ES6 has been out for a while now, and it provides reasonable solutions.


Unless javascript removes alternate meanings of `this`, which will never happen, it does not have a language-level solution. In fact, I think it's easily as reasonable a solution as lambdas. I may be biased because I think ES6 is a hot mess, but the lack of `this` binding of lambdas is a serious misstep IMO.

If anything, ES6 lambdas add yet another way of interpreting `this` because while it is a function, it's not functionally scoped. Even better, a lambda is a function but `call` and `apply` are interpreted differently [1] since `this` is not bound. If you ever need to use `call` or `apply`, you'll have to know exactly how the called code is implemented to avoid bugs.

[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


The this/that antipattern is one reason I dislike the class pattern. I avoid it by using prototypes:

  SomeClass = function (arg1) {
    // Hopefully do something more interesting than this, indeed. :)
    this.arg1 = arg1;
  };

  /**
   * Assigns foo to the instance variable bar.
   */
  SomeClass.prototype.method = function (foo) {
    // Hopefully you do something more interesting than this, too!
    this.bar = foo;
  };


  someClass = new SomeClass();
  someclass.method(foo);
Then when I need a handler for a DOM element, I bind the instance to the method:

  someClass = new SomeClass();
  var handler = someclass.method.bind(someclass);
  $('.link').click(handler); // using jQuery as easy-to-read DOM shorthand; it's not required
Or if I'm creating the handler within the class:

  /**
   * Creates a click handler for <a class="link"> elements.
   */
  SomeClass.prototype.init = function () {
    var handler = this.method.bind(this);
    $('.link').click(handler);
  };
Over the years I've found it really easy to maintain code written like this. There's no callback hell, you don't even need Promises, and everything is easy to document. But often I find myself in an environment where I'm maintaining someone else's code, and I can't just refactor everything, so I grit my teeth and use 'that' or 'self'. I like your idea a lot better.


Plus the prototype pattern is more idiomatic JS and combines better with other OO libraries than the class constructor pattern in top comment.

Also, the ES2015 class keyword is syntactic sugar for the best of both worlds of the prototype pattern, combined with the private variable support of the class pattern. It does the right thing with both `this` and `super` (which is possible but tough with the prototype pattern and nearly impossible or at least crazy tough to do right with the class constructor pattern) and is as useful/important to replacing that = this/self = this antipatterns as arrow functions are.


Why would you consider it an anti-pattern? To me, that term implies that an idiom has some sort of pitfall. Like using `==`, for instance. But `that = this` is actually pretty robust. Although, I'd say arrow functions are the better solution if all you need is a callback.

Pervasive usage of `bind` drives me mad. It can patch over problems if you're using it locally, but if a function is being passed around, it invisibly has different behavior from a normal function with dynamic `this`. It also is quite noisey, in my opinion.

Using methods of an object to contain the sequences of an async process is handy. But it's not as flexible as promises, because it only works if the async process is self-contained. Promises are far more composable, which is important for modular processes. And with `async` functions, they allow native flow control structures to cross async boundaries. By comparison, a the methods+callbacks approach feels like programing with GOTO in BASIC.


The main reason I've come to avoid prototypes in many cases (except when I really need to control memory) is that you can't keep any kind of information private (via closure) and share it across instance methods. It has to be a property on the instance, and is therefore accessible to anybody with access to the instance.

Conventions (like prefixing with _) are great, but they can only help loosely enforce encapsulation and provide no form of real privacy.


I just use factory functions that return an object that has arrow functions as properties, then I get private information via closures, and I don't have to worry about dealing with this.

https://www.youtube.com/watch?v=ImwrezYhw4w


You can combine the two, creating a closure for private variables and using proper prototypes. The easiest way now is the ES2015 class keyword and its syntax, but for older versions of JS there are patterns and practices (and about a zillion helper libraries) to do so.


For some reason, this never occurred to me. This seems so much better than `that`, `self`, etc. Thanks for sharing.


I used to do that pattern just because it was the one I found when googling how to make classes in JavaScript back in 2011. It worked until I hit scaling issues and the inefficiency of it all caught up with me, and I had to learn the prototype pattern.

Personally, I think sticking to one pattern makes more sense than having to switch patterns and deal with the inconsistency of that depending on your performance use-case. I personally think the prototype pattern is nicer, too.


It's definitely not an antipattern. "Classes" as an ES6 concept make no sense unless member functions have a uniform notion of what "this" means. As a programmer, I don't want to think about what "this" is in the concept of an ES6 class.


The dynamic binding of `this` at the callsite is one of Javascript's biggest mistakes. It's a tremendous source of run-time errors, and we have to hack around it with closures and arrow functions. How counterintuitive is that? It's exhausting to reason about what `this` refers to within a given block of code.

I very frequently want to pass methods around as callbacks and almost never want my functions re-bound as methods on alternative objects. The only times I can think of that I want to re-bind is when I'm writing library code.


The real problem is the lack of distinction between functions and methods. Dynamic "this" is really just a hack to make functions behave like methods when invoked via an object member.

Python has an interesting solution to this, which is a lot more complicated - it has a more generic concept of "descriptors" that's used to wrap functions into magic sauce that makes them methods, taking care of "self" etc - but with most of that complexity hidden from either side (definition and invocation).


I wouldn't say the Python solution is more complicated (complex, maybe), it's quite elegant and it essentially boils down to just pre-setting the first argument to the class instance or class value.

You can do it yourself:

   >> def x(*args): print(args)
   
   >> new_func = a.__get__('silly sausage')

   >> new_func()

   ('silly sausage',)
When you do instance.method() Python will look up 'method' inside 'instance'. If 'method' is a descriptor (with functions are), then `__get__` is called and the first argument is set to 'instance'

Here are the docs if anyone is interested: https://docs.python.org/3/howto/descriptor.html


Yeah, I meant descriptors themselves. Conceptually, having an ability in the language for an object to do some special handling when it's retrieved as a member of another object is a very powerful tool, and once you have it, it solves this particular problem neatly. But it's also an unusual thing to have, and few people are aware that something like this is at play every time you write something like `a.b`.


But how important is the function vs. method distinction, really? I can't think of a reason the typical higher order function should care about whether its argument is a method on some object or not. That seems like a concern that should mostly be encapsulated in the function/method.

Dynamic `this` assumes we want methods to be portable between objects. That's nice and all, but I can't think of the last time I thought it was the best approach to solving a problem in real application code.


It's solely a syntactic thing in this case - so that you can invoke it as x.foo(y) rather than foo(x, y) or x.foo(x, y). So you need some magic for the receiver to become the function argument.

It can be argued that a better way to do this is to just have freestanding generic functions that dispatch on the first argument (or better yet, multimethods that dispatch on as many as you want), and then just consider x.f(y) to be a syntactic sugar for f(x, y). But generics themselves are fairly complicated, and many people don't like that extra complexity.


Over many years of writing JavaScript, I've come to generally avoid the issues regarding `this` by preferring a functional style of programming instead of OO. I don't get away from OO completely and sometimes OO is the "goodest" solution for a problem.


In sum: "Since the...function was defined as an arrow function rather than the traditional anonymous function, and since it is defined within the lexical scope of another function X, the value of 'this' is set to the surrounding lexical scope of X [instead of the global]"


Would love suggestions here, I've migrated towards a style of always using that=this (even when not required because we're in an arrow function, for example) because the JavaScript refactoring tools I'm aware of aren't particularly robust (I'm mostly refactoring by hand) and I don't have confidence that the dev (me) moving code from one method to another will always correctly identify whether the source and destination this inheritance contexts will have the same shape. Always using that=this doesn't solve all problems (you can still get weird this's you're not expecting) but from what I think I've seen this sort of "conservative" approach seems to increase the safety of a lot of refactoring efforts in the more common use cases.

Are there sweet JS refactoring tools that a VS Code using, typescript preferring dev like myself should be using? I'd really like to drop this style if I can.


Just on case, there is a `this super` construct in Sciter's Script for that purpose:

   function Foo() {
     return function() {
       (this super).Bar(); 
     }
   } 
this super, when used in inner function, returns this passed to outer function call.


In reference to constructor invocation:

  ...a new empty object is created, (let’s name it newObj) and the function is invoked on that object,
  like say, newObj.Person() in this case.
Is this true? When a function is invoked with "new", "this" is set to the object being constructed, but is that accomplished by making the constructor a method of the object being initialized? If so, it must be removed later on in the process:

  (new Function).Function
  => undefined


It is not true. It's just a confusing attempt by the article to explain what happens to `this` inside Person().

If anything, it's more like:

    var newObj = Object.create(Person.prototype)
    var result = Person.call(newObj)
    if (typeof result === 'object' && result !== null) return result
    return newObj
The article didn't explain it like that because that requires introducing the concept of .call() on functions.


The algorithm for "var p = new Person" is roughly (ignoring prototypes):

    var p = {};
    Person.call(p); // calls Person with p as this
Remember that in js there are no "methods" just functions. The original constructor function can be accessed by "constructor" property: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


I prefer to combine arrow functions with factory functions, and avoid classes completely. You get private variables and functions via closures. The only real downside is that it's slightly slower.

Here's a good video on the subject https://www.youtube.com/watch?v=ImwrezYhw4w


The arrow function doesn't solve it in cases where you define generator functions. You cannot use the arrow operator for those causing syntax errors and you still have to keep binding "this" or define "self" like in the article.

This is one area where typescript (or es7) does it better with async functions where arrow operators are permitted.


One of my coworkers made the good suggestion that "that" is only one character away from "this", so we use "self" instead for the same purpose.


Not to be overly pedantic, but I'm I missing something here? "that" -> "this" is a two character change, no?


Ha ha of course you're right! Math is hard.


Barbie? That you?


I've always used 'me'. Not for any particular reason, it's just the first name that occurred to me.


FYI this is the keyword that Visual Basic uses as well.


It could get messy if you're doing stuff with Canvas, but I've used "ctx" in the past to denote the context change.


'self' is also the global variable when you are in a WebWorker context, so it's sort of like overwriting 'window'. When you are in a window context 'self' is also identical with 'window' but people don't usually use it there.


I use 'self' as well, but for me it was because I used Python before JS.


I haven't had to use that = this in a few years and it's immensely improved understandability of the code I work on.


if you develop for browsers, title can be: how to lose 60% of your user base to avoid one simple line.

if you are a node developer, title can be: something very banal you've been using for ages.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: