5

Given an ES6 class:

class C {
  x () { }
  fnIsMethodOfC (fn) { return /* ? */ }
}

and a variety of functions such as

function y () {}
z = () => {}

Is there an efficient way to determine if a function is a method of C i.e.

c = new C()
c.fnIsMethodOfC(c.x) === true
c.fnIsMethodOfC(C.prototype.x) === true
c.fnIsMethodOfC(y) === false
c.fnIsMethodOfC(z) === false

I know one could recursively loop through the prototype chain, but that would be an expensive operation.

Linking related questions:

Alireza
  • 2,319
  • 2
  • 23
  • 35
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
  • Do you just want to check functions the class implements itself or all functions in the Prototype chain? – Luca Kiebel Sep 02 '18 at 15:07
  • 1
    If name match is sufficient could do `Object.getOwnPropertyNames( C.prototype ).includes(fn.name)`. – charlietfl Sep 02 '18 at 15:23
  • @LucaKiebel An ideal answer would check the chain, but a suitable start would be just what the class implements. – Brian M. Hunt Sep 02 '18 at 15:42
  • If you don't know the method name, then no you will have to search for it. – Bergi Sep 02 '18 at 16:55
  • "*One could recursively loop through the prototype chain, but that would be an expensive operation.*" - why do you think that? – Bergi Sep 02 '18 at 16:55
  • @bergi The size of the prototype chain is a known unknown, with an `O(n)` lookup over the cumulative number of methods in the chain. This may be large, especially for a lookup that's in an inner-loop. – Brian M. Hunt Sep 02 '18 at 17:27
  • @BrianM.Hunt Yes, of course you would need to look at all properties if you don't know the name, but there's nothing that makes following the prototype chain especially expensive. – Bergi Sep 02 '18 at 17:54
  • @BrianM.Hunt Also, what's the actual problem you are trying to solve with this detection? I can't imagine where the check would be performance-sensitive. – Bergi Sep 02 '18 at 17:55
  • @Bergi It's part of the [lifecycle](https://github.com/knockout/tko/blob/master/packages/lifecycle/src/LifeCycle.js#L44) for TKO/Knockout.js that is the base class for binding handlers and components. For some apps lifecycle computeds can be created hundreds of thousands of times a second, so uncached `O(n)` lookups might create performance barriers. – Brian M. Hunt Sep 02 '18 at 18:17
  • @BrianM.Hunt I see, it is part of "when it's a method then bind it" magic. However, if that code runs thousands of times per second, you shouldn't create those many bound functions in the first place. Bind it once, then pass in the bound method every time you call `computed`. (Disclaimer: I never worked with Knockout) – Bergi Sep 02 '18 at 19:38
  • Thanks @Bergi. The `computed` call is user-facing, so we've no control, but pre-binding is a good tip so I'll note it in the docs re. performance. – Brian M. Hunt Sep 02 '18 at 19:44

2 Answers2

0

One option may be:

class C {
  fnIsMethodOfC (fn) {
    const proto = Object.getPrototypeOf(this)
    return proto && proto[fn.name] === fn
  }
}
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
0
class C {
  fnIsMethodOfC (fn) {
    if(!fn || !fn.name) return false;
    return !!this[fn.name] && fn === this[fn.name];
  }
}

This will also add addtional check if the actual function is different.

Amit Dhaka
  • 178
  • 4