13

I noticed that the Function.prototype has a toMethod() method in experimental JavaScript, but what does that actually do? And how do I use it?

Sebastian Zartner
  • 18,808
  • 10
  • 90
  • 132
Edwin Reynoso
  • 1,511
  • 9
  • 18
  • 1
    Perhaps related: https://esdiscuss.org/topic/any-tomethod-use-case#content-1 – Derek 朕會功夫 Dec 16 '14 at 18:45
  • I've never done any mixins or class compositions when it comes to inheritance. so I still don't understand what they are talking about or how to use it – Edwin Reynoso Dec 16 '14 at 18:52
  • 1
    @Edwin - I don't think it's meant for use by the casual JavaScript developer. It's a very deep level function for use in advanced JavaScript libraries which depend on some features of prototype inheritance that are also in "experimental" status (like the "`super()`" function) – JDB Dec 16 '14 at 18:59
  • From [another source](https://cure53.de/es6-for-penetration-testers.pdf): "The purpose of Function.toMethod() is to provide means for accessing a method from a superclass and generating a fresh function object with a new home." – Derek 朕會功夫 Dec 16 '14 at 18:59
  • Ok can someone write a quick script that uses two classes one that extends from another one and using the **.toMethod()** because when I called **.toMethod** on a function it returned a new function yet I'm not sure what changed. Like in **.bind** the context changes – Edwin Reynoso Dec 16 '14 at 19:02

2 Answers2

14

Update: the toMethod method was experimental only and did not make it into the standard. The home object is essentially static now, the only way to manipulate super is to change the [[prototype]]:

var base = {…}; // as below
var obj = Object.setPrototypeOf({
    foo() { // needs to use method definition syntax
       super.foo();
    }
}, base);
obj.foo();

It's very similar to the bind method of function objects. However, instead of creating a new function with a bound this value, it creates a new function with a bound [[HomeObject]], which is the reference that is used for super calls:

[[HomeObject]] (Object): If the function uses super, this is the object whose [[GetPrototypeOf]] provides the object where super property lookups begin.

Consider this example (not using any class syntax):

var base = {
    foo: function() {
         console.log("base foo called on", this);
    }
};
base.foo(); // base foo called on base
var obj = Object.create(base);
obj.foo(); // base foo called on obj

obj.foo = function() {
    super.foo();
};
obj.foo(); // ReferenceError: this method has no home
obj.bar = obj.foo.toMethod(obj);
obj.bar(); // base foo called on obj

obj.baz = function() {
    super();
};
obj.baz(); // ReferenceError: this constructor has no parent class
Reflect.setPrototypeOf(obj.baz, base.foo);
obj.baz(); // base foo called on obj
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I understand how to use bind and that it returns a new function with a different context but the **[[HomeObject]]** that I don't understand – Edwin Reynoso Dec 16 '14 at 18:58
  • I've now added an example on how `super` could be used – Bergi Dec 16 '14 at 19:48
  • I'm not familiar with how this all works, but it seems like it should be `obj.bar = obj.foo.toMethod(base);`, right? Aren't you setting the `[[HomeObject]]` to `base`? – JDB Dec 16 '14 at 20:38
  • @JDB: No, I'm setting the `[[HomeObject]]` to `obj`, because `super` basically means `GetPrototypeOf([[HomeObject]])` – Bergi Dec 16 '14 at 20:40
  • Ah... so to get foo to work without a reference error, you'd use `obj.foo = (function(){super.foo()}).toMethod(obj);` Or would that cause an infinite loop? – JDB Dec 16 '14 at 20:45
  • No, I think that's exactly what needs to be done (and how `obj.bar` is defined in my example) – Bergi Dec 16 '14 at 20:50
  • @Bergi was `.toMethod` deprecated? I can't seem to find it implemented in any browser. – nils May 12 '16 at 06:39
  • @nils: Yes, it's been removed from ES6 and deferred to later revisions (if at all). It seems Chrome did implement it once. – Bergi May 12 '16 at 11:14
  • The lack of `toMethod` combined with static `super` makes it really difficult to copy a method from one class to another. – trusktr Jul 26 '16 at 06:23
  • @trusktr: Yeah, it's the lack of `toMethod` that makes `super` static in the first place :-) – Bergi Jul 26 '16 at 12:53
  • @Bergi is there a way to still implement this example? now it threw error: unexpected super. it seems like the example is not a method definition thus super can't be used inside – whales Dec 21 '16 at 04:37
  • @whales hardly, see the update. You're correct, `super` outside of method definitions is a syntax error now. – Bergi Dec 21 '16 at 04:52
  • @jinbeomhong No, `Reflect` does not provide a workaround. What are you trying to do? – Bergi Jul 15 '19 at 19:19
-1

My understanding is that .toMethod is like cloning a function. Consider the example in the source I posted,

class P { }
class C extends P {
    foo() {
        console.log("f");
        super();
    }
}
P.prototype.foo=C.prototype.foo;
(new C).foo();

Here you reference a subclass method .foo in the superclass, so when you call .foo, it will reference P's .foo which is C's .foo and you have just created a loop.

It seems like to solve this issue, you can use .toMethod which "clones" the function and give it a different super/"home" that you specifed:

P.prototype.foo = C.prototype.foo.toMethod(P.prototype);

now calling (new C).foo() would not go on forever.

Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
  • ok there's only one thing I don't understand and then I'll get it. Is the **super()** representing the **class P** – Edwin Reynoso Dec 16 '14 at 19:16
  • I think now the method is belong to P so `super` would be not available at all. – Derek 朕會功夫 Dec 16 '14 at 19:25
  • before calling **.toMethod** is this the same in es5: `function P(){}` `function C(){}` `C.prototype = new P();` `C.prototype.foo = function(){ console.log('f'); P(); }` `P.prototype.foo = C.prototype.foo;` `(new C).foo()` – Edwin Reynoso Dec 16 '14 at 19:27
  • I've now studied the draft for half an hour, and came to the conclusion that you'd need to do `super.foo()` instead of just `super()` - which would only work for (constructor) functions that inherit from other functions. Can anyone confirm (test?) this? – Bergi Dec 16 '14 at 19:32
  • @Bergi - I have been thinking about that too, since super is just the constructor. – Derek 朕會功夫 Dec 16 '14 at 19:35
  • 1
    @Bergi: I know this is old and you probably know the answer by know, but just for reference: AFAIK `super()` *used* to be valid in every method and it would just call the parent method. But the spec changed recently and `super()` is now only allowed in the constructor `super.method()` is indeed the way to call parent methods in overridden methods. – Felix Kling Jan 14 '15 at 01:58
  • @Bergi: Oh :D I think I read that thread but didn't realize or remember that you asked. NVM then :) (I just stumbled upon this question while browsering the ecmascrip-6 tag). – Felix Kling Jan 14 '15 at 02:41