Have keywords 'me' and 'Me' in a class body, as aliases of 'this' and 'this.constructor' respectively

The goal is to have a shorter accessor to class members and disambiguate the meaning of this in different contexts.

Classes in ES6+ might use the keyword me as an alias of this within a class body, and the keyword Me (uppercase M) to refer to the class constructor.

Benefits:

  1. Shorten the prefix length currently required to access every member in a class body.
  2. Improve readability by avoiding the usage of this in both static and non-static scopes.
/* Example: */
class MyClass {
  static methodOne() { return Me; } // Returns a reference to the constructor, same as the static 'this'.
  static methodTwo() { return me; } // Throws an Error. No meaning for 'me' in a static scope.
  methodThree() { return Me; } // Returns a reference to the constructor, same as 'this.constructor'.
  methodFour() { return me; } // Returns a reference to the instance, same as the instance 'this'.
}
  1. Prevent the change of meaning of this in the body of a class method when such method is attached to another object.
/* Example: */
class MyClass {
  methodOne() { return 1; }
  methodTwo() { return this.methodOne() + 1; }
}
const myInstance = new MyClass();
const myOptions = {
  myMethod: myInstance.methodTwo()
};
const result = myOptions.myMethod(); // Throws an Error, as the original meaning of 'this' is changed.

I understand it may not be so straightforward to preserve the meaning of me when a method is implemented outside its defining class scope, as it may require reflection, not optimal in terms of performance.
In this case, I would still suggest to have the keywords me and Me as simple aliases of this and this.constructor respectively.

For the constructor one, that’s already been proposed: https://github.com/tc39/proposal-class-access-expressions

For the instance one, that doesn't seem like there'd be any added value. Class methods have identical semantics as functions with regard to this, so how would it reduce confusion to double the ways you can refer to it?

One thing I liked in CoffeeScript was shorthand "this" syntax they provided (as seen here). Just prefix your identifier with "@" to access a class member.

For example:

class MyClass
    x: 2
    doThing: () -> @x
 
 obj = new MyClass()
 console.log obj.doThing() # 2

I doubt there's an easy way to bring this sort of thing into JavaScript - we're already running so low on available ascii characters, it probably wouldn't be a good idea to use up another character for this minor of a shorthand. But, I thought I would bring it up, as it's related to the idea of "shortening the verbosity of referencing instance properties".

1 Like

The idea is that functions from a certain class instance, if attached to another object, would have this referring to the object and me referring to the instance, so the instance members can be accessed as they were defined in the class.
See my reply below, to better explain the difference between this amd me in the proposal, being me not always an alias of this.

Clearer illustration of point 3, where me is not always an alias of this:

/* Example, assuming keyword `me` is available in the language as described in the proposal: */
class MyClass {
  methodOne() { return 1; }
  methodTwo() { return this.methodOne() + 1; }
  methodThree() { return me.methodOne() + 2; }
}
const myInstance = new MyClass();
const myOptions = {
  failMethod: myInstance.methodTwo,
  successMethod: myInstance.methodThree
};
const fail = myOptions.failMethod(); // Throws an Error, as the original meaning of 'this' is changed.
const success = myOptions.successMethod(); // Returns the intended result (i.e. 3), as the meaning of `me` is preserved.

Currently, keyword super unambiguosly refers to a superclass instance, even when a method member is attached to another object.
This makes more certain to me that a similar keyword should be used for the instance itself, but being self taken in client Javascript, it could be me, own or some other.

You may also be interested in this related thread. I've tried to propose a better version of "this" in the past as well, but it didn't go so well: Can we fix "this"?

1 Like

What if the functions are borrowed and used on a different instance of the same class?

Would me be bound to that single instance? (in which case, the @bound decorator or a public field doing this.foo = this.foo.bind(this), and using this in the method, is your solution)

Or would it work for any instance of the same class, but fall back to the "original" instance when used with a non-instance?

There really isn't a superclass instance that super refers to. The instance is the same - whatever this is. What super does (for methods) is allow you to refer to a different implementation of a method higher in the inheritance chain. It does this by having methods keep a reference to the object in which they were defined. In the case of classes that would be the prototype for the current class. Then when a method is called from super, it finds the implementation using the prototype of that reference (prototype of the prototype).

This "home" prototype reference of a method doesn't change between instances. Instances share the same methods through the prototype and those methods will remember the same home prototype no matter what instance is using them. This allows different instances of the same class to each use super independently while nothing about the function needing to be specific to any one instance.

This is one of the problems with something like me. For each instance, me would need to be different, meaning a new version of the method would be needed for each instance so that each could independently remember what their own me needs to be.

And this is something we do today with method binding this in the constructor or assigning fields to arrow functions. Each instance gets its own method each with a this referring to that specific instance. The introduction of me wouldn't change much there.

That said, what me would do differently is allow for both the instance binding as well as the dynamic call time binding. In such a case you could use both this and me and potentially get different objects based on who is calling the method. Though I'm also not sure if we'd ever need or want that. Usually its one or the other. And if you did want that separation, you could hack something together to do that today

class MyClass {
  x = 1
  method = (me => function() {
    console.log(this.x, me.x)
  })(this)
}

const instance = new MyClass
instance.method() // 1, 1

const object = {
  x: 2,
  method: instance.method,
}
object.method() // 2, 1
1 Like

Thanks for the explanation @senocular. That's a good way of describing what I want anyway :+1:

Yes. Being me defined in the body of methods within the body of a class, it would refer to each instance of that class.

Hi @javierrey

What would you expect to happen here:

class X {
  v = 1;
  m() { return me.v; }
}

const o = {
  v: 2,
  m: X.prototype.m
};

log(o.m());

That's a good example as well, I'd expect to have result 1 , as defined inside the class.
If you use this instead of me , you get 2 , as defined in the object. The behaviour of this should not change, obviously, that would be disastrous.

Interesting, how would it know to return 1? The class instance was never created so that field initialiser was never run. Maybe this example makes that clearer:

let numberAcquired = false;
function getRandomNumber() {
  numberAcquired = true;
  return Math.random();
}

class X {
  v = getRandomNumber();
  m() { return me.v; }
}

const o = {
  v: 2,
  m: X.prototype.m
};

assert(numberAcquired === false);
log(o.m());

Not a valid option, as explained in comments below. We meed a prefix:

"Taking it a little bit further, we might not need any prefix at all". That's how it works in other languages, like Java or CSharp. Example:

class MyClass {
  x = 1
  method() { return [x, this.x]; }
}

const instance = new MyClass();

const object = {
  x: 2,
  method: instance.method,
}

console.log(instance.method()); // [1, 1]
console.log(object.method()); // [1, 2]

The behaviour of this is backwards compatible. The lack of this acts as my me proposal.

This would break existing code

const x = 'outer'
class MyClass {
  x = 1
  method() { return [x, this.x]; }
}

const instance = new MyClass();
console.log(instance.method()); // ['outer', 1] // outer is expected, not 1

Yes, that's right. It may not be as consistent as I thought, to have a lack of prefix. Thanks @senocular for the quick reply.

I see your point @aclaymore .
Maybe in this case, using the prototype and never instantiating the class, the meaning of me should fallback to the meaning of this, in which case, the result would be 2 and not 1 in your original example.

The key aspect is that to do this something has to be allocated to remember the instance. And what triggered that allocation, and when.

Java uses the :: syntax to do this:

class C {
    private int x;

    C(int _x) {
        x = _x;
    }

    void log() {
        System.out.println(x);
    }

    public static void main(String[ ] args) {
        C c = new C(42);
        Runnable r = c::log;
        // 'r' is a new allocated object
        Runnable r2 = c::log;
        assert(r != r2);
        // Each runnable stores a reference to the instance 'c'
        // and also to the original method 'log' 
        r.run(); // this can now execute and access the correct values
    }
}

Other languages like Python do this implicitly, when a method is accessed from an object it will automatically allocate a fresh function.

JavaScript is not like Python, like a method is accessed the value being returned is the original function object that all instances share. The value has no 'memory' of which instance was used to retrieve it. Instead it must be bound to the instance that it should use for it's this.

1 Like

a simpler implementation of the constructor case would be this() instead of this.constructor(). It would also be consistent with super()

What's the point of an alias for this?

Either way, both of these are already valid variable names, so they're nonstarters.