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
While class emulation is widespread nowadays, prototypes are JavaScript's true vehicle of inheritance. The following snippets of code, which contain equivalent pairs, demonstrate that prototype-based programming is more fundamental and explicit:
// define constructor and prototype// class emulation versionclassBaseConstructor{}constbasePrototype=BaseConstructor.prototype;// prototype versionconstbasePrototype={constructor(){}};constBaseConstructor=basePrototype.constructor;BaseConstructor.prototype=basePrototype;
// inheritance// class emulation versionclassChildConstructorextendsBaseConstructor{}constchildPrototype=ChildConstructor.prototype;// prototype versionconstchildPrototype=Object.create(basePrototype,{constructor(){returnbasePrototype.constructor.apply(this,arguments);}});constChildConstructor=childPrototype.constructor;ChildConstructor.prototype=childPrototype;
// instantiation// class emulation versionconstbaseInstance=newBaseConstructor();// prototype versionletbaseInstance=Object.create(basePrototype);baseInstance=baseInstance.constructor()||baseInstance;
Since prototypes are so fundamental, I believe there is space in Underscore for utility functions that make prototype-based programming more convenient. In draft, I propose the following. A real implementation would need more sophistication for ES3 compatibility, performance and possibly corner cases.
// get the prototype of any objectfunctionprototype(obj){returnObject.getPrototypeOf(obj);}// mixin for prototypes that lets you replace// var instance = new BaseConstructor()// by// var instance = create(basePrototype).init()// Of course, prototypes can also skip the constructor and directly define their// own .init method instead.varinitMixin={init(){varctor=this.constructor;returnctor&&ctor.apply(this,arguments)||this;}};// mixin for prototypes so you can replace// var instance = create(prototype).init()// by// var instance = prototype.construct()// Of course, prototypes can also directly define their own .construct method// instead.varconstructMixin=extend({construct(){returnthis.init.apply(create(this),arguments);}},initMixin);// standalone version of the construct method, construct(prototype, ...args)varconstruct=restArguments(function(prototype,args){return(prototype.construct||constructMixin.construct).apply(prototype,args);});// inheriting constructor creation for class emulation interopfunctionwrapConstructor(base,derived){returnextend(derived&&has(derived,'constructor')&&derived.constructor||base&&base.contructor&&function(){returnbase.constructor.apply(this,arguments);}||function(){},// The following line copies "static properties" from the base// constructor. This is useless in prototype-based programming, but// might be important for class-emulated code.base&&(base.constructor||null),{prototype: derived});}// mixin for prototypes with a constructor so you can replace// class ChildConstructor extends BaseConstructor {}// by// var childPrototype = basePrototype.extend({})// Of course, prototypes can also directly define their own .extend method.varextendMixin={extend(){varderived=create.apply(this,arguments);// note: using the pre-existing standalone _.extend belowreturnextend(derived,{constructor: wrapConstructor(this,derived)});}};// standalone version of the extendMixin.extend method, named differently in// order to avoid clashing with the pre-existing _.extend. inherit also seems a// more appropriate name for this function when used standalone.varinherit=restArguments(function(base,props){return(base.extend||extendMixin.extend).apply(base,props);});// collection of mixins for quick and easy interoperability with// constructor-based codevarprototypeMixins={init: initMixin,construct: constructMixin,extend: extendMixin,all: extend({},constructMixin,extendMixin)};
Note how construct and .extend/inherit are both based on create. This is no coincidence; in prototype-based programming, there is no fundamental distinction between prototypes and instances. Every object can have a prototype and be a prototype at the same time. From this point of view, .extend/inherit is just a special variant of construct that enables interoperability with class-emulated code. Without any class-emulated legacy, prototype, create and .init/construct would already cover all needs.
With the above utilities in place, we can revisit our examples from the beginning and find that the prototype-centric code is just as concise as the class-centric code:
I fairly strongly feel like Underscore shouldn't try to introduce a new system for doing OOP-in-JS, elaborated on a bit here: jashkenas/backbone#4245 (comment)
While class emulation is widespread nowadays, prototypes are JavaScript's true vehicle of inheritance. The following snippets of code, which contain equivalent pairs, demonstrate that prototype-based programming is more fundamental and explicit:
Since prototypes are so fundamental, I believe there is space in Underscore for utility functions that make prototype-based programming more convenient. In draft, I propose the following. A real implementation would need more sophistication for ES3 compatibility, performance and possibly corner cases.
Note how
construct
and.extend
/inherit
are both based oncreate
. This is no coincidence; in prototype-based programming, there is no fundamental distinction between prototypes and instances. Every object can have a prototype and be a prototype at the same time. From this point of view,.extend
/inherit
is just a special variant ofconstruct
that enables interoperability with class-emulated code. Without any class-emulated legacy,prototype
,create
and.init
/construct
would already cover all needs.With the above utilities in place, we can revisit our examples from the beginning and find that the prototype-centric code is just as concise as the class-centric code:
Related: jashkenas/backbone#4245.
The text was updated successfully, but these errors were encountered: