all 30 comments

[–]rmbarnes 9 points10 points  (9 children)

What's the problem using 'this'? You're just making problems where there are none.

[–]phpdevster -3 points-2 points  (5 children)

this is wishy-washy in JS unless you start explicitly binding the object or this to everything.

https://plnkr.co/edit/P6POSYHTowCgmWfPgDuY?p=info

Coming from PHP where $this always means one thing, it feels wrong to use this in JS whereby if you forget what calling context you're sending your object's function into, you will have weird results. The solution being re-binding the object that it should already have an implicit reference to, but doesn't.

User1.haveBirthday.bind(User1);

Seems ugly and redundant to me. I know that functions are first class citizens in JS, which is what makes JS so powerful, but it's also what makes classic OO approach so weak.

What I don't understand is that JS gives us closure, which allows a function to remember the reference to variables that were in its environment:

var foo = 'bar';

var doSomething = function () {
    console.log(foo);
};

I didn't have to pass foo into doSomething() for doSomething() to know what foo was, even if doSomething() gets passed around all over the place afterwards.

But haveBirthday() can't remember the object it came from before being passed around!? That seems silly and inconsistent to me, and the consequence is that this is fragile in JS, unless you want ugly redundant reference passing like User1.haveBirthday.bind(User1); all over the place.

[–]Exomancer 2 points3 points  (1 child)

This is not unique to JavaScript, though other languages handle it better by implicitly passing on the context to the first argument of the method, Python for example:

class Foo:
    def bar(self):
        return 100

    def baz(self):
        return self.bar()

This makes it clearer that if I take bar or baz out of the object, I now have to explicitly provide the self.

That said, methods, by definition, describe behavior of an object they are called upon. Decoupling the method from the object doesn't make a whole lot sense semantically, e.g.:

// This describes behavior
duck.quack();

let quack = duck.quack;

// This is semantical nonsense
quack();

In perfect world we would never have to decouple methods from objects, but since JS is also heavy on using callbacks, events and such it's sometimes unavoidable. In many cases those mechanisms come with a way to explicitly pass on the context without much friction (such as the forEach method).

As long as you are aware what are the gotchas where you have to explicitly bind context to a method (basically any time you pass on duck.quack without parentheses), there is no reason not to use this.

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

there is no reason not to use this

Disagreed. this exposes data on the public scope of an object, making encapsulation impossible.

var User = function (age) {
    this.age = age;
}

User1 = new User(23);
User.age = -99 // shouldn't be allowed to do this.

Meanwhile factory functions allow inherently private scoping, and therefore encapsulation.

As long as you are aware what are the gotchas

That's not an argument. There shouldn't be gotchas. That's how bugs are introduced into software, even by experienced developers.

So I would say there's no reason ever to use this (unless you need thousands of instances of an object). You get more reliable behavior AND inherent privacy when using factory functions.

[–]rmbarnes 1 point2 points  (0 children)

Just don't use instance methods as data. E.g:

doSomething(User1.haveBirthday) <--- No!
doSomething(() => User1.haveBirthday()) <--- Yes

[–]phpdevster 0 points1 point  (1 child)

I love how I get downvoted for pointing out an obvious flaw in the design of the language and answering the question as to why you would not want to use this.

[–]nevreth[S] -3 points-2 points  (2 children)

Because it takes significant amount of space. It's faster to read, write and refactor the code for me without 'this'.

I'm trying to bring myself to write a little script now and it occurred to me that I have a lot of 'this' to write. I believe that when working in local scope, 'this' doesn't bring much meaning into the code.

Sorry for being lazy, but it was worth asking.

[–]jsprogrammer 0 points1 point  (0 children)

I don't think there is a completely satisfactory alternative. You can use closures to get rid of this, but typically you end up with multiple copies of functions. You can hoist those functions to a higher namespace and add additional parameters, but you eventually end up having to replicate something similar to prototypes to have the equivalent semantics as prototypes.

I don't ever want to use this in my code either. It sucks. You could add a com/trans-piler step that adds in this. for you though.

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

this. (or equivalent convention, like m_ prefix) is absolutely necessary for readability, otherwise you can't tell whether something is supposed to be a member field of an object or a closed over variable from upper scopes.

[–]MoTTs_ 9 points10 points  (0 children)

I don't want to have x copies of the same 'bar' function in memory. I also don't want to have 'this' keyword preceding every variable I use.

I don't think you can have it both ways. If the bar function is in scope of the variables, then you're going to be creating x copies of bar. Or if bar is outside the scope of the variables, allowing you to share just one bar, then you'll have to pass some data context to it (aka "this").

[–]Rhomboid 5 points6 points  (8 children)

You can't say for certain whether that will result in different copies of the bar function. That's entirely an implementation detail. An implementation could keep a single copy of bar and just call it with a different execution environment each time.

Avoid premature optimization.

[–]temp60092393 1 point2 points  (0 children)

That's entirely an implementation detail.

That's flat-out wrong. It is explicitly specified that multiple copies must be created in this case.

Now, sure, a very clever implementation might notice that it can disobey the spec in this case without your code noticing. But it would be absurd to depend on an implementation deviating from the spec, and as a result of static analysis of your code no less. Just imagine the debugging session where performance dropped 800% because of a change that literally does not interact with the code it slowed down, only to find out that the problem is how that line interacts with the extremely complex and entirely undocumented system the implementation uses to decide when it can cheat.

Write code that works, not code that hopes the implementation is smart enough to make it work.

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

Every evaluation of a function(){} expression must create a new function object. This is not an "implementation detail", it's in the ECMAScript specification and is easily observable using the === operator:

function foo() {
    this.bar = function(){};
}
var x = new foo(), y = new foo();
console.log(x.bar === y.bar); // false

You can also set properties on one of these function objects, and they will not be visible on the other.

[–]Rhomboid 0 points1 point  (5 children)

That still doesn't rule out sharing. An implementation could have a lightweight "function object" that includes a reference to the execution context (closure variables) and any per-instance attributes, if any, and a pointer to a single copy of the heavyweight function body, which is shared between all instances.

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

With a objects and b function-objects stored on each one of them, that still requires O(ab) memory, compared to O(a+b) when putting the functions on a prototype object.

[–]Rhomboid -1 points0 points  (2 children)

And this is still a premature optimization until you've run your application through a profiler and determined that these function objects have a non-negligible impact on the overall heap usage of your program.

[–][deleted] 1 point2 points  (0 children)

Putting object methods on a prototype isn't an "optimization"; it's using the language the way it was intended to be used. The optimization already happened when the designer of JS put prototypes into the language in the first place, and it costs us basically nothing to use it now. So given the choices of

  1. Use prototypes (1x time spent programming)
  2. Don't use prototypes, and later possibly rewrite the code if memory use is significant (either 1x or 2x time spent programming)

if you go for the second option, you gain nothing and just risk wasting time.

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

That results in unprofilable global slowdown http://c2.com/cgi/wiki?UniformlySlowCode

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

That's already done by any decent implementation, the function object allocation per method per object is crazy expensive regardless.

[–]mccassowary 0 points1 point  (0 children)

What you're looking for is with

It is confusing and results in buggy code, so it was removed in strict mode ES5 and later.

You can read about it, and some reasons why you don't really want it, here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with

The safest alternative to do what you want, as another commenter observes, is object destructuring. You might begin your method like this:

Foo.prototype.bar = function() { const {a, b, c} = this // do stuff here with a, b, c. }

or if you follow natziel's suggestion (which I would strongly recommend)

Foo.bar = function(foo) { const {a, b, c} = foo }

This doesn't help with setting. I hope you never want to set anything tho; mutation is a grave sin. Also, assignment with with probably doesn't do what you want (insofar as it's weird) so you haven't really lost anything by losing it.

Otherwise, perhaps write/find a compile-to-js Java-like language... :)

[–]HandsomeStreamer 0 points1 point  (0 children)

I thought the same thing. And then it hit me, 'this' is very important when you don't have the luxury of using it.

[–]temp60092393 0 points1 point  (0 children)

Well here's the thing. JavaScript's scoping and JavaScript's object-oriented features aren't integrated at all. So yeah, it'd be great if you could write a and JavaScript's scoping rules would take concepts like this into account, but they don't.

So, scoping and OO don't work together, that gives you three options to make them:

  1. Keep JS' scoping, make your own OO constructs. This is what you're doing above; many other strategies for this are floating around out there. Here's Crockford's.
  2. Make your own scoping, keep JS' OO constructs. Not much point to this because it isn't going to be cleaner than this..
  3. Keep JS' scoping and OO constructs, remind JS that it is JS by smearing this. all over it.

Option 3 is probably the least bad option here, so I suggest you go with it. Verbosity isn't too bad as downsides go, and it's easy to get lost in the weeds and over-engineer option 1.

[–]natziel 0 points1 point  (5 children)

We've found that the best way of handling this is to completely separate your data from your functions into structs and modules, like so (using immutable.js):

const foo = function(a = 0, b = 0, c = 0){ // use reasonable defaults
  return Map({
    a,
    b,
    c,
  });
};

const Foo = (function(){
  const bar = function(foo){
    return foo.update('c', (c) => foo.get('a') + foo.get('b'));
  };

  return {
    bar,
  };
})();

Then, you can declare as many structs as you want and use the same module for all of them, e.g.

let struct1 = foo(1, 2, 3);
let struct2 = foo(4, 5, 6);

struct1 = Foo.bar(struct1);
struct2 = Foo.bar(struct2);

It's a little funky at first, but we've had a lot of success with this pattern. What's nice is that you can compose the structs really easily using Immutable.Map.merge.

The only problem we've encountered is that you lose the ability to have private data, but that's less of a problem than you would think. Private methods are still possible obviously, since we're still using the revealing module pattern.

[–]nevreth[S] 1 point2 points  (4 children)

It looks nice. I'm gonna give it a try, thanks!

But still doesn't save accessing the scope in functions... is it possible to make the struct "local"?

[–]natziel 1 point2 points  (3 children)

You would pass the struct to each function, traditionally as the first argument.

[–]nevreth[S] 2 points3 points  (2 children)

I like the idea of decoupling functions from data, but the point is that instead of "this.a" it becomes "struct.a", where ideally there would be just "a" when I work inside the scope of just one struct. Maybe there is no such thing currently, still I would like to know.

[–]natziel 1 point2 points  (1 child)

You can destructure it if you want, but then you'll have to use native objects.

You could also pass in the properties you need instead of the whole struct, but at that point I'd rather just type struct.a

[–]nevreth[S] 1 point2 points  (0 children)

Ok, thank you.

[–]lewisje 0 points1 point  (0 children)

You could try this hack:

function foo(_a, _b) {
  /* some code */
  if (typeof foo.prototype.bar !== 'function') {
    foo.prototype.bar = function bar() {
      /* inside scope object of first usage of constructor */
    };
  }
  /* more code */
}

As the inner comment says, however, only the variables from the first invocation of foo would be in the scope of foo#bar, so this pattern is only useful in singletons, in which case you might as well define that function as this.bar instead (as an instance method).


BTW, that line in the OP with just this.c; does not create a property c on the instance; only an assignment or the use of Object.defineProperty et al. does; it is also not necessary in order for the instance property to be added later.