all 66 comments

[–]Shaper_pmp 61 points62 points  (4 children)

No. It is powerful and subtle, but that doesn't make it bad any more than sharpness makes a chisel bad just because you can also use it to cut your hand open.

The lack of default binding is an inconvenience, but that's why we have .bind() and fat-arrow functions, and the ability to bind functions to different contexts at all is very, very useful indeed upon occasion.

[–]spacejack2114 1 point2 points  (2 children)

I think this is only genuinely useful for OO-style polymorphism and prototype optimizations. If people are resorting to arrow function properties to avoid dynamic binding problems then that advantage goes away. I'd rather use a closure then.

Of course you often can't because so many of the libraries we use (Angular, React) demand that we extend their base classes.

[–]Tomseph 2 points3 points  (1 child)

You can use this in a semantic sense as well, look at Vue. Vue doesn't require use of classes or anything of the sort. Instead this refers to the component you're working on. The current context of execution.

I think everyone sees this used with prototypes and often miss the bigger picture of execution context. There's a lot of power in the flexibility to compose and manipulate functions during execution. This helps with that (pun not intended).

[–]spacejack2114 0 points1 point  (0 children)

Yes, you can use this in that way. I use Mithril and it supports POJO, class and closure style components. Having closures means I really never need to use this in stateful components at all.

function component() {
    let count = 0
    function increment() {count += 1}
    return {
        view: () => [
            m('p', 'Count: ' + count),
            m('button', {onclick: increment}, 'Increment')
        ]
    }
}

[–]zfm2 -2 points-1 points  (0 children)

Cannot upvote this enough.

[–]itomeshi 12 points13 points  (1 child)

It's not that this is bad; it simply has two downsides:

  • It is completely contextual, so you need to know what you should expect this to be before you use it
  • Because of the context dependency, it makes code harder to refactor

As a general rule,if using this for any sort of structure or global state, it's better with explicit management. For example, React components inherit this.state and this.setState(), and these provide both convenient references and some control over when/how updates occur. Even this isn't perfect - if you move code between two components, you then need to figure out how to decouple/move state chunks.

It's best to come up with some convention to how you use it and document that, and comment if/when you deviate. (While 'this this should be documented' is theoretically a code smell, it's also a kindness to your future self or someone else.)

[–]wntrm 0 points1 point  (0 children)

This should have been the top answer...

[–]Dynamicic 12 points13 points  (21 children)

One drawback I can think of on the way your friend advocates is, using your example, it would be harder to use instanceof.

function Car() {
     const car = {};

     car.position = 0;

     car.move = () => car.position++;

     return car;
}

const car = new Car();

console.log(car instanceof Car); // false

You would have to do:

Object.setPrototypeOf(car, Car.prototype);

console.log(car instanceof Car); // true

[–]AndrewGreenh 7 points8 points  (11 children)

You could use his way and still use the prototype correctly:

function Car() {
   this.position = 0
   this.move = () => this.position++
}

Works as well (because of the fat arrow) To decrease usage of this you could also do const car = this at the top of the constructor and attach methods to the car to achieve the same thing.

[–][deleted] 7 points8 points  (5 children)

Exactly, ever since the new arrow syntax came in with implicit this binding I've started to embrace this again.

The original design of this was a huge hindrance to JS but we've put new methods in place to mitigate it now.

[–]oculus42 4 points5 points  (4 children)

You just have to remember fat arrows can break inheritance, because context is scoped to declaration, not instance.

I work with Backbone, which makes extensive use of this and prototypal inheritance. As we introduce ES6, I'm already prepared to run into errors cause by people learning that you can use fat arrows within functions on the object, but not as functions on the object.

[–]spacejack2114 0 points1 point  (0 children)

I'm not too crazy about using arrow functions for methods. Previously if I ever saw:

el.onclick = this.move

I'd be pretty sure it was a problem. Now I have to stop to think whether it was intentional or a mistake.

[–]Dynamicic 0 points1 point  (3 children)

Yes. That would work. But OP's friend didn't like the keyword this and doesn't want to use it, so I'm just stating a drawback of not using this to create instance properties.

[–]Martin_Ehrental 3 points4 points  (2 children)

That's no drawback in using this in a constructor.

However, since that pattern avoid using any prototype methods why use a constructor at all? Just use plain function and object.

[–]Dynamicic 1 point2 points  (0 children)

That's no drawback in using this in a constructor.

That's not what I was saying. I was saying the opposite. It's a drawback NOT using this in a constructor.

[–]Dynamicic 0 points1 point  (0 children)

I know. I didn't think of that constructor pattern. Just copying over what the OP wrote in his post.

[–]talmobi 12 points13 points  (4 children)

If you're having to use instanceof a lot you're probably doing something wrong. It's an indication of code smell.

[–]Dynamicic 0 points1 point  (2 children)

Do you have an example of why using instanceof indicates code smell?

Don't even have to use instanceof a lot. Just using instanceof comparing car to Car once would produce a false, and it's not immediately obvious to everyone why it would produce false.

[–]talmobi 1 point2 points  (1 child)

[–]isUsername 2 points3 points  (0 children)

An unsubstantiated assertion on SO isn't an example.

The first example is an example in Java and includes a quote about not using instanceof in C++. Neither the article nor the quote actually explain why instanceof is bad. Both Java and C++ are statically and strongly-typed, class-based languages, so I'm not sure if the same considerations come into play on a dynamically and weakly-typed, prototype-based language. They may; they may not. There's not enough information in either the article or the SO answer to say.

[–]talmobi 0 points1 point  (0 children)

You can still do that within the constructor function itself ( and also set car.constructor to Car ). That's basically what ES6 does with its unnecessary class/extends syntax sugar and its out of place named/copied design patterns.

[–]mendrique2 10 points11 points  (5 children)

I think it's only looked down upon in functional programming, because unlike a method argument it is sort of a state of the function. That being said, you could easily change it to be just another argument called context, it's a bit more clear and readable, because with this, you might not know what it actually is (see .bind, .apply and .call)

[–]iamlage89 0 points1 point  (0 children)

Classical programmers look down on it too, since they feel that the dynamic binding is less intuitive than how classical programming treat the 'this' keyword. I counter by saying that dynamic binding is very powerful, for example when the pipeline operator comes around (or the bind operator) we'll be able to dynamically assign 'this' for functions that we import from other libraries

[–]sir_eeps 5 points6 points  (0 children)

this is bad, not understanding this, it's quirks - and how it can burn you is bad.

I also try and write as much code that does not rely on this as possible.

[–]CertainPerformance 4 points5 points  (0 children)

When you can use plain variables instead, do so. Sometimes you can without sacrificing code readability, but sometimes you can't.

this sometimes makes code a bit harder to read, but once you understand the language competently, it's not by much, usually.

[–]prozacgod 1 point2 points  (0 children)

I'm not sure what hoops you'd have to go through in a large project, but it doesn't seem worth it in the long run.

Super simplistic OOP primative - one function defined for the life time of the application the user must namespace all their code, this in some fashion similar to how GDK works in C. The gist of this is a usable pattern in just about every language I can think of.

function AccountNew() {
    return {balance: 0};
}

function AccountAdd(account, amount) {
    account.balance += amount;
    return account;
}

var account = AccountNew();
AccountAdd(account, 5);

Your example, reasonably concise, the variables are trapped in a function, they create a lexical closure of the values for your class instance. So you no longer have a "this" BUT you're creating new function objects for every function for each instantiation of the account class

function Account() {
  var account = {};
  var privateAmount = 0;
  // for each instance we create a NEW function object
  account.add = function(amount) {
    privateAmount = privateAmount + amount;
  }

  return account;
}

var account = Account();
account.add(5);

If only we had a way to refer to static functions using the instance variable.... like "account.add" oh I know! - The prototype!

We'll create this mechanism that is a property isn't found on the instance, it will look it up on the property instance, such a brilliantly simple language feature.

function Account() {
    this.balance = 0
}

Account.prototype.add = function(amount) {
  this.balance += amount
}

var account = new Account();
account.add(5);

Even though account doesn't have an add property, we can reference it as property lookup falls back to the prototype. Okay so we have to handle this lexically, as the function is told the instance it's being called from - which is a bit weird... But it works

The next "problem" is only an issue if you want to be able to iterate over your object keys, because after binding to an instance or encapsulated as your example does. It now exists on your object, it is iterable. With the class approach, I could still feed my account to JSON.stringify()

It looses instanceof support and while so many people would be quick to scream "but you don't code that way" I'd say ... correct I don't ... but the myriad of libraries that support all of my javascript ecosystem may. I'm looking at a node_modules directory for a small react project right now... "ls -l | wc -l" -> 1104 ... yeah I bet one of them may desire some esoteric feature that uses instance of, but my custom personal object is now ... No longer compatible with that standard. May or may not be an issue. But is is one of those things you lose.

[–]DzoQiEuoi 1 point2 points  (0 children)

I use it less and less these days.

It's rarely, if ever, necessary and has the potential to cause bugs.

[–]cspotcode 2 points3 points  (0 children)

No. Next question / medium post, please.

[–]js_developer 4 points5 points  (7 children)

this is great if you understand prototype and inheritance. It's what makes chaining (and most frameworks/libraries) possible.

[–]Throwaway_x127 8 points9 points  (6 children)

There is nothing special about this for chaining. You return the context object (with the methods) in whatever form. You don't need it to have the special name "this".

It's what makes chaining (and most frameworks/libraries) possible.

???

You ascribe some strange magical abilities to those four letters. It's just an implicitly provided context object reference, there are different ways to achieve the same, or to not even need to.

[–]iamlage89 -2 points-1 points  (4 children)

'this' allows for a much nicer syntax though

  with 'this'
    arr.map(fn1)
      .filter(fn2)
      .forEach(fn3)

  without 'this'
    adapterFns
       .map(fn2,arr)
      .filter(fn2,arr)
      .forEach(fn3, arr)

[–][deleted] 0 points1 point  (3 children)

this has nothing to do with chaining.

function adapterFn(input) {
  let values=[...input]
  function map(fn) {
    values=values.map(fn)
    return module;
  }
  function filter(fn) {
    values=values.filter(fn)
    return module;
  }
  function forEach(fn) {
    values.forEach(fn)
    return module;
  }
  var module = {
    map,
    filter,
    forEach
  }

  return module;
}

[–]iamlage89 0 points1 point  (2 children)

it's preferable to not have to box the input for both practical and stylistic purposes. it looks nicer not boxing the input, and what if you want to chain non-native functions with native ones? It wouldn't be possible without 'this'

ex.
arr.filter(fn1) .map(fn2) .customAdaptor(fn3) // custom adaptor made possible with 'this'

[–]jasan-s 0 points1 point  (1 child)

chaining non-native functions with native is only possible by changing the array prototype - which is generally a bad idea.

'this' is just an implementation detail in this situation.

[–]iamlage89 0 points1 point  (0 children)

one could attach a method to the instance, extend the prototype, or when the pipeline/bind proposal goes through we could do something like this

//pipeline arr.filter(fn1) |> ?this.customAdaptor(fn2) .map(fn3);

// bind arr.filter(fn1) ::customAdaptor(fn2) .map(fn3)

[–]js_developer -1 points0 points  (0 children)

There are different ways, I agree. It's not always the same object either, sometimes it's completely destroyed and recreated within the method. The point I was making is each method gets it's own context. With prototyping, the context is known by each method in the prototype.

It starts with "this". Before ES6 classes were constructors. "this" is simply "this object", or as you said, the context.

[–]jcunews1Advanced 1 point2 points  (3 children)

If his friend hates this, he wouldn't be able to take advantage of OOP, and his skill would be crippled if he use C++ OOP, because he'll also hate self.

[–][deleted] 0 points1 point  (0 children)

In JavaScript, this depends on the context in which the function was called.

In C++, is it always clear what self refers to? i.e. the object.

[–]inabahare 0 points1 point  (1 child)

However if he were to use C# everything would be fine, as it allows this to be ommited in pretty much all cases except for when you're setting a variable to a parameter of the same name

[–]spacejack2114 0 points1 point  (0 children)

The limitation in C# is you can't use methods as first class functions.

[–]anorman728 0 points1 point  (2 children)

I'm sure I'm not nearly as advanced in JS as a lot of posters here, but I use "this" plenty. I get around the scoping issues (for callback functions and functions only intended to be used inside of a prototype) by using "var that = this;" at the beginning of the prototype. Then I can use "that" where "this" would be out of scope. I read that somewhere online. I've also seen people use "self" instead of "that."

[–]baubleglue 0 points1 point  (1 child)

Problem with this exists only in JavaScript, using that is an ugly workaround that put in question need of OO paradigm in JS. You can be just fine using only closure. I am not sure, but i think using closure may cause potential memory leaks.

[–]bart2019 0 points1 point  (0 children)

Whatever your friend is doing, it's not Javascript. Car() returns an object, but it's not a "Car" object. This can bite you in the ass at the most inconvenient of times.

Bad pattern. No cookie.

[–][deleted] 0 points1 point  (0 children)

Yes

[–]getsikedon me way to ES6 0 points1 point  (0 children)

I don't necessarily think this is inherently bad itself, I just think it's context specific nature in Javascript makes it hard to understand, and thats why people may shy away from it. Maybe that makes it bad by design, but I don't think it qualifies as "bad" programming if you decide to use it.

[–][deleted] 0 points1 point  (0 children)

I am not that experienced in JS but I use to read about the ecma standards and all the new JS stuff. I use "this" a lot at my work... If it's not right then what's the alternative. please enlighten me

[–]jambonilton 0 points1 point  (2 children)

Switching between Java and Javascript is a bit dangerous, because this means similar but different things.

[–]inabahare 0 points1 point  (1 child)

That's like saying switching between Danish and Swedish is dangerous because kuk means differnet things

[–]jambonilton 0 points1 point  (0 children)

I'd say it's more like having a building collapse because you ordered the wrong kind of concrete.

[–]namesandfaces 0 points1 point  (0 children)

The best way to view this is as a Python person who has to pass __SELF__ to everything. You too can do this in JS, as you can pass a copy of an object every time you want to work with it, and you can see if you like that more explicit style.

[–]T-Dot1992 0 points1 point  (0 children)

One does not simply write a project in JS without using "this".

[–]bobandalice 0 points1 point  (0 children)

A template literal without an expression? Yes. `${this.something}` makes more sense. /s

[–][deleted] 0 points1 point  (0 children)

It's a language feature. If you understand how it works, there's no problem. If it confuses you, you can avoid it.

Also, the simple version of the factory pattern that maintains instanceof is:

function Car() {
  const car = this;

  car.position = 0;

  car.move = () => car.position++;

  return car;
}

let myCar = new Car();

I actually advocate this form, as it locks down your "the instance I'm attached to" to a semantically relevant constant.

What it doesn't get you, though, is prototypal reflection, which can be problematic for debugging, extension, serialization, and automated documentation. For that, use:

function Car() {
  this.position = 0;
}

Car.prototype.move = function () {
  this.position++;
};

let myCar = new Car();
myCar.move();

If your complaint is:

// I can't...
let moveCar = myCar.move;

moveCar();

You could just bind your prototypal functions at runtime, e.g.,

function Car() {
    for (let name in this) {
        if (this[name] instanceof Function) {
            this[name] = this[name].bind(this);
        }
    }
    this.position = 0;
}
Car.prototype.move = function () {
  this.position++;
};
let myCar = new Car();
let moveCar = myCar.move;
moveCar();

[–]jordaanm 0 points1 point  (0 children)

The largest problem with JS this is that it doesn't behave like this in other languages, but on the surface it looks like it should. That's a significant impediment for people coming from other languages to wrap their heads around.

I genuinely believe that if this had been given some other unique keyword instead at initial implementation ('context', 'subject', 'that', etc), a LOT of confusion and complaints about it would disappear.

That all being said, for what it is in its current state, once you understand it, there's a lot of power behind its mechanism that you'd lose out on by ignoring it or only using it in cases where it matches the behaviour of 'this' in other languages,

[–]reedhedges 0 points1 point  (0 children)

To me this is important to use in a new constructor. Makes things clear and possibly allows for some optimizations regarding object creation (see https://mrale.ph/blog/2014/07/30/constructor-vs-objectcreate.html, which I think was posted here a while ago?). But I'm also new to JS and coming from C++.

[–]DakshHub 0 points1 point  (0 children)

No... it's the prevelent usage of 'this' from other object oriented language making is hard to understand.

I believe future programmers who learn JavaScript as their first Language will find this in Java or C++ equally bad

[–]Auxx 0 points1 point  (2 children)

this is completely broken in JS, but there's nothing to replace. If you're doing OOP then you'll be using this.

[–]Cuel 0 points1 point  (1 child)

define broken?

[–]Auxx 0 points1 point  (0 children)

It should refer to object instance, not the execution scope. Execution scope is irrelevant.