stampit-org / stampit

OOP is better with stamps: Composable object factories.
https://stampit.js.org
MIT License
3.02k stars 102 forks source link

Converting *to* a function constructor for compatibility #310

Closed far-blue closed 7 years ago

far-blue commented 7 years ago

I'm new to stamps and I'd like to start using them but have existing code that uses constructors to create objects on demand. Is there a recommended way to achieve this?

My initial thinking was to wrap the stamp-generated object creating function in another function that acts as the constructor for use with 'new' but I assume this will break the prototype chain.

I guess what I'd need would be a function that can act as a constructor that sets the resulting object prototype to an object from the stamp but which then also runs the initializers.

Any thoughts?

koresar commented 7 years ago

Short answer:

Long answer:

This article might help: https://medium.com/@koresar/fun-with-stamps-episode-1-stamp-basics-e0627d81efe0 Closer to the end it also explains .prototype of stamp-made object instances.

If after reading you have questions - please shoot them here.

Cheers :)

koresar commented 7 years ago

Oh, btw. Stamps work with the new keyword and without it.

var s = require('@stamp/it');

var S = s({props: {a:1}});

console.log( S() ); // { a: 1 }
console.log( new S() ); // { a: 1 }

console.log( Object.getPrototypeOf(S()) === Object.getPrototypeOf(new S()) ); // true
far-blue commented 7 years ago

Great to know I can still use 'new' if needed to work with older libraries that don't use Object.create() etc.

Whether we like it or not, a lot of js code expects 'new' to work and so saying "Don't use new" isn't overly helpful.

I certainly wouldn't be surprised if I was missing something here as I'm pretty new to all this but to my understanding 'new' is effectively just creating a new object, setting its prototype and then running the content of the constructor function in the new object's context (i.e. 'this' is the new object). So, really, it is allowing for a form of delegation inheritance through the prototype chain. As such, is it really a bad thing? Certainly when creating thousands of objects with the same methods delegation inheritance is recommended.

I assume using 'new' with a stamp function correctly uses delegation on the prototype chain to prevent copying of methods each time. Or is that assumption wrong?

koresar commented 7 years ago

Sorry not taking enough time to answer your questions.

I'd HIGHLY recommend to read my article - https://medium.com/@koresar/fun-with-stamps-episode-1-stamp-basics-e0627d81efe0 4 minutes read. Half of the questions you asked are answered there.

Here is why stamp's philosophy is to avoid new keyword - https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3

Stamps do not allow chained (vertical) inheritance. (Chain means more than one connection.) But stampit does use the .prototype to share methods between the object instances. It's all in my "Fun with stamps" article. I call stamp's inheritance model - horizontal.

Your assumption is wrong. The answer is in the same "Fun with stamps" episode.

Feel free to shoot more questions.

koresar commented 7 years ago

If you are good at reading JavaScript than you can understand the internal mechanics of Stamps from this little article: https://medium.com/@koresar/fun-with-stamps-episode-4-implementing-stamps-in-30-loc-e52f5c17dcfe 5 minutes read.

The core is basically 30 lines of code. Should answer almost 100% of your questions. :)

far-blue commented 7 years ago

Thank you :)

In your 'fun with stamps' article you mention, in the section 'Objects created by stamps', that the prototype for the objects created by the stamp function is the .methods object from the stamp descriptor which is itself a property of the stamp function. This would suggest my assumption is correct and all objects created by the stamp function use prototype based delegation and the methods are not copied. So it won't be a problem to create thousands of objects from the stamp function.

If I understand the code in ep4 of 'fun with stamps', properties are copied, methods are delegated via the prototype and initializers are run as part of the createFactory() method as described in the section 'Factory function internals'.

Maybe I worded my initial assumption incorrectly. I'm not interested in a hierarchy of chained prototypes. I'm interested in correct use of delegation so that when I create thousands of objects from the same stamp function I'm not creating thousands of copies of all the methods on the objects at the same time. I've seen examples of mixin- and extend-style code that do exactly this and it's a huge memory hog and performance killer :)

I am surprised 'new stampFunction()' works but pleased and although the use of 'new' is discouraged I think people new to stamps and working with mixed codebases would like to know it is possible and what, if any, differences exist between 'stampFunction()' and 'new stampFunction()'.

koresar commented 7 years ago

I was very exciting reading your last comment @far-blue ! :)

Regarding the new keyword with stamps.

Consider this code:

function CreateRabbit() {
  return Object.create({ isRabbit: true });
}

var regularObj = CreateRabbit();
var newObj = new CreateRabbit();

console.log( Object.getPrototypeOf(regularObj) === Object.getPrototypeOf(newObj) ); // true

There is a single difference. While creating rabbit via new the JS engine would create an empty object and assign it as the function's context. The context is ignored by the function. Whereas, without new the function will be called without any context.

This means that creating object from stamps with new keyword would generate a useless object. Thus putting some unnecessary pressure to the GC.

koresar commented 7 years ago

Closing the issue as resolved. Please open if any.

far-blue commented 7 years ago

thank you for all your help :)

ericelliott commented 7 years ago

Some informative reading on the topic of new, prototype delegation, etc...

Why newing stuff is probably a bad idea... mixed codebase or no:

Class vs Prototypes vs Composition:

Everything is Composition

The essence of software development is composition. You can't get away from composition even if you try. If you do try to get away from composition, you end up composing anyway -- but you'll do it badly.

koresar commented 7 years ago

Liked your recent article @ericelliott :) Waiting for the continuation!

ericelliott commented 7 years ago

@koresar Have you read the rest of the series? There are a bunch of installments now...