简体   繁体   中英

Composition vs Inheritance and their costs in Javascript

I just found out about composition in javascript, I always thought inheritance is the standard way of writing javascript. What I don't get is, if I use a class and inherit for example two methods from another class, every time I create a new instance and console log it, the object only has its "local" properties, no sign of the inherited methods, which is obvious. But, now if I do the same with composition, every time I create a new object and I console log it, every new created object now also has the methods.

Are we not duplicating code, you know, the same methods over and over again? , in different objects. Am I missing something here?

inheritance (and repeating the OP)

An instance does not own inherited methods itself. If called with such a method, the interpreter does start looking up the prototype chain for a matching method. Once the method was found, it gets invoked within the context of the very instance which before was this method's target object.

Thus instances do not feature own slots/properties for inherited methods but rely on JavaScript's built-in automatic delegation mechanism, hence inheritance based on prototypal delegation.

 class FirstLastItemList extends Array { // inheritance via prototype methods. first() { return this[0]; } last() { return this[this.length - 1]; } } const list_A_1 = new FirstLastItemList("A", "B", "C"); const list_A_2 = new FirstLastItemList(2, 3, 4, 5, 6); const list_B_1 = new FirstLastItemList("D", "C", "B"); const list_B_2 = new FirstLastItemList(6, 5, 4, 3, 2); console.log('list_A_1.first():', list_A_1.first()); console.log('list_A_2.first():', list_A_2.first()); console.log('list_B_1.last():', list_B_1.last()); console.log('list_B_2.last():', list_B_2.last()); console.log('\n'); console.log( '(list_A_1.first === list_B_2.first)?', (list_A_1.first === list_B_2.first), '...inherited code' ); console.log( '(list_A_2.last === list_B_1.last)?', (list_A_2.last === list_B_1.last), '...inherited code' ); console.log('\n'); console.log( 'Object.getOwnPropertyNames(list_A_1):', Object.getOwnPropertyNames(list_A_1) ); console.log( 'Object.getOwnPropertyNames(FirstLastItemList.prototype):', Object.getOwnPropertyNames(FirstLastItemList.prototype) );
 .as-console-wrapper { min-height: 100%;important: top; 0; }

exemplary composition techniques via function based mixins (and answering the OP)

Are we not duplicating code, you know, the same methods over and over again? , in different objects. Am I missing something here?

The answer in short is... "Yes, but one should be aware of and one also can still control the footprint or memory usage that comes with different composition techniques."

First of all, with composition one always does add (an) own property(ies) to an object. For any newly assigned method one can either provide a full blown implementation to it or just a reference to the latter.

This is true too for the two most common forms of mixin based composition, which is, any glue code based either on Object.assign or on function based mixins.

The provided example does choose the function based mixin approach in order to promote this form of code reuse (but of cause the same idea applies to its object based counterpart)...

 function firstListItemMixinWithCodeDuplication() { // - duplication of both, slot and implementation/code. this.first = () => this[0]; } function last() { // implement code once... return this[this.length - 1]; } function lastListItemMixinWithSharedReference() { //... then reuse via reference... // - duplicated slot but shared implementation/code. this.last = last; } const list_A_1 = ["A", "B", "C"]; const list_A_2 = [2, 3, 4, 5, 6]; const list_B_1 = ["D", "C", "B"]; const list_B_2 = [6, 5, 4, 3, 2]; firstListItemMixinWithCodeDuplication.call(list_A_1); firstListItemMixinWithCodeDuplication.call(list_A_2); lastListItemMixinWithSharedReference.call(list_B_1); lastListItemMixinWithSharedReference.call(list_B_2); console.log('list_A_1.first():', list_A_1.first()); console.log('list_A_2.first():', list_A_2.first()); console.log('list_B_1.last():', list_B_1.last()); console.log('list_B_2.last():', list_B_2.last()); console.log('\n'); console.log('list_A_1.first:', list_A_1.first); console.log('list_A_2.first:', list_A_2.first); console.log( '(list_A_1.first === list_A_2.first)?', (list_A_1.first === list_A_2.first), '...applying closed code' ); console.log('\n'); console.log('list_B_1.last:', list_B_1.last); console.log('list_B_2.last:', list_B_2.last); console.log( '(list_B_1.last === list_B_2.last)?', (list_B_1.last === list_B_2.last), '...applying shared code' ); console.log('\n'); console.log( 'Object.getOwnPropertyNames(list_A_1):', Object.getOwnPropertyNames(list_A_1) );/*... ... console.log( 'Object.getOwnPropertyNames(list_B_2):', Object.getOwnPropertyNames(list_B_2) );*/
 .as-console-wrapper { min-height: 100%;important: top; 0; }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM