Last active
October 20, 2015 18:21
-
-
Save joliss/8a3968c354acd7e3497d to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| // Sketch for exposing [[Call]] and [[Construct]] machinery to JavaScript | |
| // 1. First, let's add the ability to make any object callable with a __call__ | |
| // property (using double underscores for some quick strawman syntax). | |
| // Let __target__ be the object that is being called. | |
| var container = { obj: {} }; | |
| container.obj.__call__ = function() { | |
| console.log('this = ' + this + ', __target__ = ' + __target__); | |
| }; | |
| // container.obj is now callable: | |
| container.obj(); | |
| // => this = container, __target__ = obj | |
| // 2. Second, let's make object construction (`new`) overridable with | |
| // __construct__: | |
| // Default `new` implementation, from http://www.2ality.com/2014/01/new-operator.html | |
| Function.prototype.__construct__ = function(...args) { | |
| // Create `this` and call the function. | |
| var thisValue = Object.create(__target__.prototype); | |
| var result = __target__.apply(thisValue, args); | |
| if (typeof result === 'object' && result !== null) { | |
| return result; | |
| } | |
| return thisValue; | |
| }}; | |
| // 3. Now that we have __call__ and __construct__, we can use it to express | |
| // the current `class` semantics. Let `Class` be the metaclass of all classes | |
| // created with the `class` keyword. | |
| // handles `C()` | |
| Class.prototype.__call__ = function() { | |
| throw new TypeError("Class constructors cannot be invoked without 'new'"); | |
| }; | |
| // handles `new C()` | |
| Class.prototype.__construct__ = function(...args) { | |
| // Unlike Function.prototype.__construct__, we don't try to call the class | |
| // itself - that would throw a TypeError. Rather, we call the constructor | |
| // function, which we've stowed away at, say, class.__constructor__: | |
| var thisValue = Object.create(__target__.prototype); | |
| var result = __target__.__constructor__.apply(thisValue, args); | |
| if (typeof result === 'object' && result !== null) { | |
| return result; | |
| } | |
| return thisValue; | |
| }; | |
| // 4. Now `Date` is expressible as a Class like so: | |
| class Date { | |
| static __call__() { // similar to `call constructor` | |
| // Instead of throwing a TypeError, we return a string: | |
| return 'Tue Oct 20 2015 16:37:21 GMT+0000 (UTC)'; | |
| } | |
| constructor(year, month, day, hour, minutes, seconds, milliseconds) { | |
| // ... | |
| } | |
| } | |
| // 5. Finally, for the extremely common case where we want calling a class to | |
| // be some form of instantiation: Here's how to enable newless instantiation | |
| // for a class hierarchy deriving from OurBase by implementing __call__: | |
| class OurBase { | |
| static __call__(...args) { | |
| return new __target__(...args) | |
| } | |
| } | |
| class OurSub extends OurBase { } | |
| OurSub() // => OurSub instance | |
| // [Ignore this 6th point, I don't think there's consensus for this!] | |
| // 6. Bonus: If we're feeling ambitious, we can even make newless class | |
| // instantiation allowed-by-default in ES7. This is probably | |
| // backwards-compatible, and I think it's very sensible, but some people might | |
| // find it objectionable: | |
| // ES6 disallows `C()` | |
| Class.prototype.__call__ = function() { | |
| throw new TypeError("Class constructors cannot be invoked without 'new'"); | |
| }; | |
| // ES7 allows `C()`, unless overridden by subclass | |
| Class.prototype.__call__ = function(...args) { | |
| return new __target__(...args) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment