Concise Method Binding

# JD Isaacks (8 years ago)

Considering the proposals for both concise methods and the bind operator I think it would be a great addition to be able to use them together.

I am already seeing a lot of this:

class Foo {
  bar = () => {
    // bound
  }
  buz() {
    // unbound
  }
}

I think having the bind operator with a concise method makes things more uniform:

class Foo {
  ::bar() {
    // bound
  }
  buz() {
    // unbound
  }
}

This would also allow for this to be using on object literals:

let foo = {
  ::bar() {
    // bound
  }
  buz() {
    // unbound
  }
}

This would also make using recursion with concise functions feasible:

let fibonacci = {
  ::at(n) {
    if (n < 2) {
      return n;
    }
    return this.at(n-1) + this.at(n-2);
  }
}
fibonacci.at(7); // 13

I am looking for a champion for this feature. Anybody interested?

# Jordan Harband (8 years ago)

In your first example, the arrow function's "this" would be the "this" of the context the "class" is defined in - it wouldn't be bound at all. Can you point me to an example of where that example "bar" is a function bound to the instance?

# Bergi (8 years ago)

Jordan Harband schrieb:

JD Isaacks <jd at jisaacks.com> wrote:

class Foo { bar = () => { // bound } buz() { // unbound } } In your first example, the arrow function's "this" would be the "this" of the context the "class" is defined in - it wouldn't be bound at all.

No, the methods defined with "=" are automatically moved into the constructor, so that they are created once for each new instance. this in the arrow function would in fact be bound to the instance.

It's indeed super-confusing to "declare" such methods right inside the class though, that's why I'm really opposing that proposal (jeffmo/es-class-static-properties-and-fields).

Greetings, Bergi

# JD Isaacks (8 years ago)

Bergi, I agree and I think this proposal negates the need for assigning methods like that.

# Rick Waldron (8 years ago)

On Mon, Nov 9, 2015 at 8:45 PM JD Isaacks <jd at jisaacks.com> wrote:

Considering the proposals for both concise methods and the bind operator I think it would be a great addition to be able to use them together.

I am already seeing a lot of this:

class Foo { bar = () => { // bound } buz() { // unbound } }

Can you clarify what this is, because it's not valid ES6 code. Also, "bound" and "unbound" in what sense? In buz, the this object will be the instance of Foo from which it's called.

# Isiah Meadows (8 years ago)

It's using an ES7 proposal, and it's a method bound to the instance.

# Rick Waldron (8 years ago)

On Tue, Nov 10, 2015 at 6:59 PM Isiah Meadows <isiahmeadows at gmail.com>

wrote:

It's using an ES7 proposal, and it's a method bound to the instance.

Ah, right—that proposal does not have consensus: rwaldron/tc39-notes/blob/master/es7/2015-09/sept-22.md#54-updates-on-class-properties-proposal .

# JD Isaacks (8 years ago)

So far all the comments have been regarding my first example using ES7 class properties proposal. No one has actually even mentioned the the part I am proposing. Is my explanation just poor?

# Andrea Giammarchi (8 years ago)

are you talking about this? zenparsing/es-function-bind#ecmascript

# JD Isaacks (8 years ago)

Andrea, Sort of. I am talking about adding an additional place where that operator :: can be used -- with concise methods.

Currently they cannot be used in the way I described above but I think there are several reasons why it makes sense.

# Andrea Giammarchi (8 years ago)

Just my thoughts, I wouldn't put any self-bound thing in the class and rather improve that :: proposal so that whenever you obj::method it creates a uniquely bound callback so that obj::method === obj::method and at that point whenever you need to export, pass, or use such method you just obj::method or obj::method() or let method = obj::method and bring the pattern whenever it's needed instead of being slightly different per each "place" (class rather than objects)

That would make it lazy, usable for events (in order to be able to also remove them) and easily transpilable for smoother migration.

Having class A { ::method() {} } feels like somebody is playing too much with the protoype or "accidentally" polluting the constructor

# JD Isaacks (8 years ago)

I think what you are suggesting already exists with ::obj.method which evaluates to obj.method.bind(obj)

However, this creates a new function each time so ::obj.method !== ::obj.method, not sure how ::obj.method === ::obj.method would work.

I sort of agree with you that using it that way would be preferred. However if the community wants bound methods attached to objects, there is currently no way to do that with an object literal.

You would have to do something like:

let obj = {};
obj.method = function(){}.bind(obj);

With my proposal you can.

let obj = { ::method(){} };
# Andrea Giammarchi (8 years ago)

Yeah, I've got that, my point is that whenever you need it you just obj::method

Your example indeed says that currently the proposal is that obj::method is similar to obj.method.bind(obj) which is indeed always different and indeed you want something that makes method always the same/unique bound one, which I believe is universally the preferred way.

What are two different bound of the same method useful for? Pretty much nothing, IMHO, while having a shortcut to lazily obtain a single bound version of that method for that object can be useful in many ways, as example obj.on('event', anotherObj::method) where it's always possible at that point to obj.removeListener('event', anotherObj::method) in case its needed.

Having a shortcut that all it does is replace something already short to write like a .bind feels to me like a missed opportunity.

Moreover, with this improvement you won't need/care to have self-bound methods at all

let obj = { method(){} };

// and whenever needed you use
obj::method;

Hope my POV is more clear now.

# JD Isaacks (8 years ago)

Yes your point of view is more clear now, I like this is a lot.

But I do not know how that would be transpiled or what the desugared version would look like. However, that would be awesome as you described.

A thing to note. You keep using obj::method which is different from ::object.method the former is when method is not already attached to the object, the later is for then it is.

An example:

let foo = function(){};
let bar = {};

bar::foo // foo.bind(bar);

verses

let bar = { foo(){} };

::foo.bar // foo.bar.bind(foo);

I think both cases theoretically would be awesome to work as you described. Just fuzzy on how it would look underneath.

# JD Isaacks (8 years ago)

Pardon, I meant:

let bar = { foo(){} };

::bar.foo // bar.foo.bind(bar);

in my last example.

# Andrea Giammarchi (8 years ago)

The way it could work is similar to the following one:


(function (wm){'use strict';

  // just to show the possible internal slot mechanism
  Object.prototype.boundTo = function (method) {
    // since I've used this for ages now in eddy.js, just replicating
    var fn = typeof method === 'function' ? method : this[method];
    var bound = wm.get(this);
    if (!bound) wm.set(this, bound = {fn:[], bound:[]});
    var i = bound.fn.indexOf(fn);
    if (i < 0) bound.bound[i = bound.fn.push(fn) - 1] = fn.bind(this);
    return bound.bound[i];
  };

}(new WeakMap));


// example
var obj = {method: function () { return this; }};

// now, whenever needed
obj.boundTo(obj.method);

// will create the slot and set it up with obj.method
// so that the following is true
obj.boundTo(obj.method) === obj.boundTo('method') &&
obj.boundTo('method')() === obj;

// if it's about another method
// the equivalent of this
::obj.anotherMethod

// whould be
obj.boundTo(anotherMethod);

The string fallback is not needed or relevant, it's just a semantic shortcut in my example to reach the method through the object without repeating the object name

# Bergi (8 years ago)

Andrea Giammarchi schrieb:

Just my thoughts, I wouldn't put any self-bound thing in the class and rather improve that :: proposal so that whenever you obj::method it creates a uniquely bound callback so that obj::method === obj::method

This was considered: zenparsing/es-function-bind#17 - it's quite unexpected that an operator would return the same result every time, and there are security considerations as well. Also it would be quite complicated to spec - how and where did you store the memoisation. Feel free to join the discussion!

Using the :: operator in the class declaration itself makes sense to me. It conveys "this method will always be bound" very effectively, and you wouldn't even need special syntax to access it.

class Xample {
   ::myListener(…) {…}
}

should desugar to

class Xample {
   // a getter on the prototype
   get myListener() {
     // with per-instance memoisation
     return this.myListener = () => {
       // that returns a bound method
       …
     };
   }
}

It might be equivalently done via a custom decorator of course:

class Xample {
   @autobind
   myListener(…) {…}
}

, Bergi

# JD Isaacks (8 years ago)

I like this very much. I would prefer this to my recommendation. So how to we go about proposing it as a change to an existing proposal?

# JD Isaacks (8 years ago)

Ahh, good point Bergi. Thanks for brining that up.

# Gilbert B Garza (8 years ago)

Forgive me if I'm missing the point, but isn't the entire purpose of using classes to make all instances share the same function in memory via this ? If you want methods to always be bound, why not use closures instead?

function Person (name) {
  var person = this
  person.greet = function () {
    return person.name + " says hi!";
  };
}

var alice = new Person('Alice');
alice.greet() //=> "Alice says hi!";

var f = alice.greet;
f() //=> "Alice says hi!";
# Bergi (8 years ago)

Gilbert B Garza schrieb:

Forgive me if I'm missing the point, but isn't the entire purpose of using classes to make all instances share the same function in memory via this ? If you want methods to always be bound, why not use closures instead?

function Person (name) {
   var person = this
   person.greet = function () {
     return person.name + " says hi!";
   };
}

Yes, the point of prototypes is sharing. But maybe we don't want (cannot use) that. The point of the class syntax is just to enable a more declarative definition of class methods - which should include some way to create instance methods as well, simply because they're needed often enough.

And being able to declare methods that will get bound outside of the constructor (even if that's a bit confusing) avoids much boilerplate again. Just compare

class Person extends{
   constructor(name) {
     super(name);
     this.greet = () => {
       return this.name + " says hi!";
     };
   }
}

vs

class Person extends{ // with default constructor
   ::greet() {
     return this.name + " says hi!";
   }
}

, Bergi

# Brendan Eich (8 years ago)

Your Person::greet example is canonically used to demonstrate dynamic |this|. How does it motivate the instance-bound method in class use-case? Perhaps show some example that justifies the method-per-instance overhead.

# Gilbert B Garza (8 years ago)

Ah ok, I understand now; if you're using the class syntax, you want to be able to arbitrarily and declaratively bind methods to the instance. Makes sense.

I also prefer your suggestion to delegating this functionality to a decorator; ideally the decorator would be built into the language.

# Andrea Giammarchi (8 years ago)

You basically demonstrated nobody read my comment in there months ago ;-)

zenparsing/es-function-bind#17

Anyway, the security concern is non existent to me, every method of every instance of every class can be a communication channel, everything bound can be a communication channel, I don't see why ::obj.someMethod would be different and is not explained anywhere in there.

But since nobody read that thread anymore, I guess it's pointless to even keep writing in there.

Best

# Andrea Giammarchi (8 years ago)

FWIW, I've added my final considerations in there, hoping that's remotely useful to anyone. zenparsing/es-function-bind#17

Best

# JD Isaacks (8 years ago)

I also prefer your suggestion to delegating this functionality to a

decorator; ideally the decorator would be built into the language.

I think using a decorator to get this functionality is a great way to achieve it if it were to not be built into the language. However, I think if it were to be built into the language, my bind operator makes more sense.

The bind operator is already being used, for 3 forms of method binding:

// 1
foo::bar(...args);
// equivalent to
bar.apply(foo, args);

// 2
foo::bar
// equivalent to
bar.bind(foo)

// 3
::foo.bar
// equivalent to
foo.bar.bind(foo)

With all these already being added in only makes my suggestion the more natural addition. And will make code more easy to recognize at a glance.

Plus all of the existing uses of the bind operator are just syntactic sugar for something you can already do, hence my equivalents shown above. However there is still no way to bind a method to an object declaratively in an object literal. Currently, you must first create the object, then bind a method to it.