-
-
Save anba/9f0acbb29bf755d26f37 to your computer and use it in GitHub Desktop.
ECMAScript 6: automatically binding extracted methods (http://www.2ality.com/2013/06/auto-binding.html)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// http://www.2ality.com/2013/06/auto-binding.html | |
(function(global) { | |
const Function_bind = Function.prototype.call.bind(Function.prototype.bind); | |
function autoBind(obj) { | |
const handler = { | |
get(target, name, receiver) { | |
const result = Reflect.get(target, name, receiver); | |
if (typeof result != 'function') { | |
return result; | |
} | |
return Function_bind(result, receiver); | |
} | |
}; | |
return new Proxy(obj, handler); | |
} | |
const boundPrototypes = new WeakMap(); | |
function getBoundPrototypeFromConstructor(constructor) { | |
const proto = constructor.prototype; | |
if (!boundPrototypes.has(proto)) { | |
boundPrototypes.set(proto, autoBind(proto)); | |
} | |
return boundPrototypes.get(proto); | |
} | |
class BoundClass { } | |
BoundClass[getSym("@@create")] = function() { | |
return Object.create(getBoundPrototypeFromConstructor(this)); | |
} | |
Object.defineProperty(BoundClass, getSym("@@hasInstance"), { | |
value(obj) { | |
if (obj != null && typeof obj == 'object') { | |
const boundProto = getBoundPrototypeFromConstructor(this); | |
if (boundProto === Reflect.getPrototypeOf(obj)) { | |
return true; | |
} | |
} | |
return Function.prototype[getSym("@@hasInstance")].call(this, obj); | |
} | |
}); | |
class Person extends BoundClass { | |
constructor(name) { | |
this.name = name; | |
} | |
describe() { | |
return `I'm ${this.name}`; | |
} | |
} | |
class Employee extends Person { | |
constructor(name, title) { | |
super(name); | |
this.title = title; | |
} | |
describe() { | |
return `${super()} (${this.title})`; | |
} | |
} | |
global.Person = Person; | |
global.Employee = Employee; | |
// tests | |
let jill = new Person('Jill'); | |
let jillDescribe = jill.describe; | |
let john = new Employee('John', 'CFO'); | |
let johnDescribe = john.describe; | |
print(jill.describe()); // prints "I'm Jill" | |
print(jillDescribe()); // prints "I'm Jill" | |
print(john.describe()); // prints "I'm John (CFO)" | |
print(johnDescribe()); // prints "I'm John (CFO)" | |
print(jill instanceof Person && !(jill instanceof Employee)); // true | |
print(john instanceof Person && john instanceof Employee); // true | |
})(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment