Concise Method Binding
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?
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
Bergi, I agree and I think this proposal negates the need for assigning methods like that.
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.
It's using an ES7 proposal, and it's a method bound to the instance.
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 .
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?
are you talking about this? zenparsing/es-function-bind#ecmascript
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.
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
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(){} };
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.
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.
Pardon, I meant:
let bar = { foo(){} };
::bar.foo // bar.foo.bind(bar);
in my last example.
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
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 youobj::method
it creates a uniquely bound callback so thatobj::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
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?
Ahh, good point Bergi. Thanks for brining that up.
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!";
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
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.
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.
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
FWIW, I've added my final considerations in there, hoping that's remotely useful to anyone. zenparsing/es-function-bind#17
Best
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.
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?