You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I previously fiddled around with the new new Foo('foo')('bar') pattern by extending Function. However, after working with more reasonable code my brain rebelled once again and remembered an odd fact about the new operator: if the function called as a constructor returns an object, that object becomes the result of the new operator, not the constructed instance!
This is deceptively simple until you look closer, in which case it becomes a recursive pile of dumb:
classRenewable{// We want to set an initial value for `constructor.values` just to make// later code cleaner.staticgetvalues(){return[];}// A perfectly ordinary constructor on a perfectly (not) ordinary base class.constructor(value){// Begin BS: since this is a base class, we can reference `this` without// a call to `super()`. `this` will be the constructed/ing instance,// and as an instance it will have a reference to its constructor.// AN IMPORTANT NOTE: If this instance is an instance of a _subclass_,// `this.constructor` will be a reference to that _subclass's constructor_,// not to the base class!constBase=this.constructor;// We defined `values` as `static`, so it's attached to the constructor.constprevValues=Base.values;// Remember, a Class is referenced by its Constructor Function, so we can// just use that Constructor off of `this` as the base class for this new class...// NOTE: This is a _return statement_! We're not just defining a subclass// of the current instance's class, we're also replacing the result of// the `new` operator by returning some object other than `this`!returnclass$RenewableextendsBase{// In each subclass, we return all the values from the base class plus the// next value sent through the constructor.staticgetvalues(){return[...prevValues,value];}// A seemingly innocuous subclass constructor, which just passes the value// on to the super class's constructor...constructor(nextValue){// ... except that it's continuing the trainwreck of madness by returning// the result of super()!returnsuper(nextValue);}};}}
So what happens each time we call new Renewable('Foo')?
Renewable's constructor function is called with 'Foo'
Base is set to this.constructor, which the first time around will be Renewable itself.
prevValues is set to Base.values, which the first time is Renewable.values, which is [].
A new class $Renewable is defined that extends Base, which right now is just Renewable itself.
That new class $Renewable is returned instead of the newly constructed instance, which results in new returning that subclass rather than the constructed instance of Renewable.
At that point, if we checked .values we'd see an array: ['Foo']
What happens with new new Renewable('Foo')('Bar') then?
First, all of the above occurs, causing the first new operator to return that subclass constructor.
Then, the next new operator calls that returned constructor with 'Bar'.
The fun begins with $Renewable's constructor calling super(nextValue): This goes up to the original Renewable constructor.
In that constructor, this.constructor is now $Renewable's constructor!
And Base.values will then be $Renewable.values, which is ['Foo'].
And then... Renewable's constructor creates another subclass, this time of the first $Renewable class!
... And then returns that sub-sub-class, continuing the unvirtuous cycle.
Thus, from that, new new Renewable('Foo')('Bar').values will be ['Foo', 'Bar'].
Because each class just returns another subclass, this can be done as far as you want!
Edit: The Same Pattern, but Fewer Class Keywords
Despite the trickery to support the class keyword, Classes in JS are still essentially functions, albeit rather funky ones.
This extra-new pattern however is trivial to implement even without this newfangled feature:
constRenewable=(()=>{functionRenewable(value){constBase=this.constructor;constprevValues=Base.values;function$Renewable(nextValue){returnRenewable.call(this,nextValue);}// Uncomment if you actually care about the prototype chain...// $Renewable.prototype = Object.create(Base.prototype);// $Renewable.prototype.constructor = $Renewable;Object.defineProperty($Renewable,'values',{get(){return[...prevValues,value];},});return$Renewable;}Object.defineProperty(Renewable,'values',{get(){return[];},});returnRenewable;})();
The text was updated successfully, but these errors were encountered:
I previously fiddled around with the
new new Foo('foo')('bar')
pattern by extendingFunction
. However, after working with more reasonable code my brain rebelled once again and remembered an odd fact about thenew
operator: if the function called as a constructor returns an object, that object becomes the result of thenew
operator, not the constructed instance!This means we can write a far more fun example:
Which means you can now write the most obnoxious chained call that no one ever wanted:
This is deceptively simple until you look closer, in which case it becomes a recursive pile of dumb:
So what happens each time we call
new Renewable('Foo')
?Renewable
's constructor function is called with'Foo'
Base
is set tothis.constructor
, which the first time around will beRenewable
itself.prevValues
is set toBase.values
, which the first time isRenewable.values
, which is[]
.$Renewable
is defined that extendsBase
, which right now is justRenewable
itself.$Renewable
is returned instead of the newly constructed instance, which results innew
returning that subclass rather than the constructed instance ofRenewable
.At that point, if we checked
.values
we'd see an array:['Foo']
What happens with
new new Renewable('Foo')('Bar')
then?new
operator to return that subclass constructor.new
operator calls that returned constructor with'Bar'
.$Renewable
's constructor callingsuper(nextValue)
: This goes up to the originalRenewable
constructor.this.constructor
is now$Renewable
's constructor!Base.values
will then be$Renewable.values
, which is['Foo']
.Renewable
's constructor creates another subclass, this time of the first$Renewable
class!Thus, from that,
new new Renewable('Foo')('Bar').values
will be['Foo', 'Bar']
.Because each class just returns another subclass, this can be done as far as you want!
Edit: The Same Pattern, but Fewer Class Keywords
Despite the trickery to support the
class
keyword, Classes in JS are still essentially functions, albeit rather funky ones.This extra-
new
pattern however is trivial to implement even without this newfangled feature:The text was updated successfully, but these errors were encountered: