tc39 / proposal-class-public-fields

Stage 2 proposal for public class fields in ECMAScript
https://tc39.github.io/proposal-class-public-fields/
487 stars 25 forks source link

Could we do something about *this* ? #37

Open dfahlander opened 8 years ago

dfahlander commented 8 years ago

Could we do about the repeated use of _this_ keyword in class centric Ecmascript code?

Looking at any class centric ES2015 or typescript you will see _this_ being used a lot. IMO this is a problem because it harms the readability and increases the file size. A colleague of mine's reaction over our typescript code was "Somebody has to do something about this" and I agree. The essence of javascript has been small, readable and unbloated code. And we all know the success of that.

Couldn't this proposal also define that closure lookups should include class property checking?

class Person {
    firstName;
    lastName;
    age;

    constructor (firstName, lastName, age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    hello() {
        return `Hello ${firstName} ${lastName}!`;
    }

    birthday() {
        ++age;
    }
}
fatfisz commented 8 years ago

Wouldn't it be similar to this?

with (this) {
  ...
}

with is deprecated (ES6 modules use strict mode by default, and so the use of with is forbidden) and the reasons for this would also apply to your idea (http://www.2ality.com/2011/06/with-statement.html). Basically it's unclear what are we referring to until runtime, so that can introduce more pain than it would cure.

If you want to reduce your usage of this, maybe use

Object.assign(this, { firstName, lastName, age });

instead?

dfahlander commented 8 years ago

Even though similar to with keyword, the suggestion is not do use an implicit with in methods. My suggestion is to make it super clear at parse time what it resolves to, exactly the same way that closures work. That's very different from how with works. with suffers from not knowing at parse time what property access would refer to. Closures on the other hand, does not suffer from that. Just think of the class field declaration as a closure and that's it. Intuitive and clear.

class Person {
    // class-field "closure":
    firstName;
    lastName;
    age;

    hello() {
        // Here, we benefit from refering to class-field closures and can skip 'this'.
        return `Hello ${firstName} ${lastName}!`;
    }

    birthday() {
        // Here, we also benefit from refering to class-field closures and can skip 'this'.
        ++age;
    }
}

// A sub class would live in its own closure:
class Clown extends Person {
    hatColor;

    doTricks() {
        // Here, we can refer to hatColor, without specifying `this` but
        // when refering to properties defined at super class, we would
        // still need to use `this` because we are not in it's closure.

        console.log(`My name is ${this.firstName}! Watch me doing some tricks:`);

        hatColor = "red";

        setTimeout(()=>{
            hatColor = "blue";
        }, 1000);

    }
}
fatfisz commented 8 years ago

Ok, I though "class property checking" referred to checking properties on the instance (this).

Doesn't that introduce a problem with naming arguments though? I think that writing something like this:

method(_value) {
  value = _value;
}

in order to avoid clashing with the field name looks messier than

method(value) {
  this.value = value;
}

And also for me the readability is lost, because value I'm assigning to is an object property, but that fact is hidden, just like in the case of with. What do you think?

ljharb commented 8 years ago

"Increases the file size" is a non-concern, both because it's negligible, and because gzip takes care of it regardless. As for "readability", I firmly believe that implicitness is what harms readability, not the presence of tokens. Using "this" is strictly superior to having it be implied, imo.

tofagerl commented 8 years ago

We're not talking about removing this entirely, are we? If it's just a matter of removing it in the constructor so we don't have to do arg = this.arg all the time, couldn't that be solved via syntax in the constructor parameter like the spread operator. In other words, if you've got a class with constructor(foo, bar, baz) today, we could do something like constructor(foo, @bar, baz) tomorrow, where bar automagically gets a reference copied to this.bar, while foo and baz don't. Obviously the syntax is borrowed from coffescript, but I'm not married to it.

In fact, this wouldn't have to be class specific....

ljharb commented 8 years ago

@tofagerl that'd be a separate proposal imo.

dfahlander commented 8 years ago

I realize that my proposal would not be in line with how methods are accessed, as standardized in ES2015.


function doSomething () {
}

class Foo {
    someProp;

    doSomething () {
    }

    bar () {
        doSomething(); // Refers to outer function as of ES2015, not our method.
        someProp = 3; // If this line refers to property, we'd be inconsistent!
    }
}

I still think it's a pitty though, but too late to change.

tofagerl commented 8 years ago

Why would we want to access someprop without this anyway? We'd be confusing everyone by changing established practices.

yanickrochon commented 8 years ago

I agree with @tofagerl. This will only create confusion and take away code readability. Seriously, is code size really a problem? Coming from a TypeScript user, those *.d.ts files take A LOT more space than a few this inside a class implementation. The argument has not a lot of weight, here.

Also, as @dfahlander explained, this is required if the property is coming from a parent prototype... so why making it optional if it creates an exception when using properties? It makes no sense to me and is everything but clearer.

aluanhaddad commented 8 years ago

Looking at any class centric ES2015 or typescript you will see this being used a lot. IMO this is a problem because it harms the readability and increases the file size. A colleague of mine's reaction over our typescript code was "Somebody has to do something about this" and I agree. The essence of javascript has been small, readable and unbloated code. And we all know the success of that.

@dfahlander I think the problem is that people are using classes more than they should be. I am an avid TypeScript user and I only use classes where frameworks expect them.

This is about programming style.

Tangentially, regarding verbosity, TypeScript itself requires very few type annotations and works best when you let the inference flow as far as possible. I rarely declare variables without assigning to them (I love const) so most of my TypeScript code looks very close to ES2015 code