Skip to content

Instantly share code, notes, and snippets.

@getify
Last active May 2, 2022 16:10
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save getify/86bed0bb78ccb517c84a6e61ec16adca to your computer and use it in GitHub Desktop.
Save getify/86bed0bb78ccb517c84a6e61ec16adca to your computer and use it in GitHub Desktop.
creating hard-bound methods on classes
class Foo {
constructor(x) { this.foo = x; }
hello() { console.log(this.foo); }
}
class Bar extends Foo {
constructor(x) { super(x); this.bar = x * 100; }
world() { console.log(this.bar); }
}
bindMethods(Foo.prototype);
bindMethods(Bar.prototype);
var x = new Foo(3);
x.hello(); // 3
setTimeout(x.hello,50); // 3
var y = new Bar(4);
y.hello(); // 4
y.world(); // 400
setTimeout(y.hello,50); // 4
setTimeout(y.world,50); // 400
var method = (function defineMethod(){
var instances = new WeakMap();
return function method(obj,methodName,fn) {
Object.defineProperty(obj,methodName,{
get() {
if (!instances.has(this)) {
instances.set(this,{});
}
var methods = instances.get(this);
if (!(methodName in methods)) {
methods[methodName] = fn.bind(this);
}
return methods[methodName];
}
});
}
})();
function bindMethods(obj) {
for (let ownProp of Object.getOwnPropertyNames(obj)) {
if (typeof obj[ownProp] == "function") {
method(obj,ownProp,obj[ownProp]);
}
}
}
@billiegoose
Copy link

Suggestion #1: Don't bind functions that are already bound. Usually for freshly constructed objects this won't be the case, but hey this is JavaScript so anything could happen! 😆

function bindMethods(obj) {
   for (let ownProp of Object.getOwnPropertyNames(obj)) {
-      if (typeof obj[ownProp] == "function") {
+      if (typeof obj[ownProp] == "function" && !obj[ownProp].name.startsWith("bound ")) {
         method(obj,ownProp,obj[ownProp]);
      }
   }
}

@billiegoose
Copy link

billiegoose commented Mar 5, 2019

Suggestion #2: I'm going out on a limb here but I think this works...

// New "base class"
class Binder {
  constructor () {
    bindMethods(this.constructor.prototype)
  }
}

// Make Foo extend that base class
class Foo extends Binder {
   constructor(x) { super(x); this.foo = x; }
   hello() { console.log(this.foo); }
}

// Bar extends Foo and inherits the autobinding behavior automatically
class Bar extends Foo {
  constructor(x) { super(x); this.bar = x * 100; }
  world() { console.log(this.bar); }
}

// Original demo
var x = new Foo(3);
x.hello();  // 3
setTimeout(x.hello,50);   // 3

var y = new Bar(4);
y.hello();  // 4
y.world();  // 400
setTimeout(y.hello,50);   // 4
setTimeout(y.world,50);   // 400

@P0lip
Copy link

P0lip commented Mar 5, 2019

- for (let ownProp of Object.getOwnPropertyNames(obj)) {
+ for (let ownProp of Reflect.ownKeys(obj)) {

Don't forget about Symbols!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment