GeorgieF
GeorgieF

Reputation: 2727

JavaScript prototype chaining with simple handling of this using 'self'

I began to like the following pattern (sry, Coffeescript here cause it's more readable in that case):

Parent = (proto)->
  self = Object.create proto
  public1 = ->
    console.log "in public1 with self: ", self
    console.log "in public1 with name: ", self.name

  self.public1 = public1
  self

Child = (proto)->
  self = new Parent proto

  private1 = ->
    console.log "in private1 with self: ", self
    console.log "in private1 with name: ", self.name
    self.public1()

  public2 = ->
    console.log "in public2 with self: ", self
    console.log "in public2 with name: ", self.name
    private1()

  self.public2 = public2
  self

GrandChild = (proto)->
  self = new Child proto

  public3 = ->
    console.log "in public3 with self", self
    console.log "in public3 with name: ", self.name
    self.public2()

  self.public3 = public3
  self

felix = new GrandChild name: "Felix"
felix.public2()

That naive attempt of multiple inheritance works and enables a simple and obviously convenient use of 'self' ... smart when you're coming from other oop languages like I do.

Gotcha: every GrandChild object creates a NEW Child as well as a NEW Parent object, so that memory consumption grows in case many GrandChild objects are created.

Augmenting GrandChild's prototype with Child's and Parent's methods would only reference their methods in a GrandChild object as far as I understood (and save lots of space), but reading up and down I do not find a way to have access to self the way I have with the upper solution.

I know Coffeescript itself provides a class based inheritance system on top of JS's prototypal inheritance. Other libs provide solutions as well. I'd like to simply understand what's going on to pick the right solution depending on the use case.

What if - for example - I would like to put private1 and public2 of Child into a prototype, so that these functions are referenced instead of copied?

Can someone enlighten me at that point?

Upvotes: 0

Views: 250

Answers (1)

Alex Wayne
Alex Wayne

Reputation: 187034

Gotcha: every GrandChild object creates a NEW Child as well as a NEW Parent object, so that memory consumption grows in case many GrandChild objects are created.

False. What you are doing here, say when you new GrandChild, is that you instantiate a Parent and then adorn it with methods from the Child contructor, and then again from the GrandChild constructor. It's the same object, from Object.create proto all the way through the grandchild. In that respect, you aren't littering up your context with dummy object at all.

Augmenting GrandChild's prototype with Child's and Parent's methods would only reference their methods in a GrandChild object as far as I understood (and save lots of space), but reading up and down I do not find a way to have access to self the way I have with the upper solution.

Closure based private methods and prototypes don't usually get along. You can have a lot of flexibility when you do it all in constructors like this, but you lose the advantages of using the prototype (like speed from not redefining functions in new scopes all the time).

I know Coffeescript itself provides a class based inheritance system on top of JS's prototypal inheritance. Other libs provide solutions as well. I'd like to simply understand what's going on to pick the right solution depending on the use case.

It does, and when using coffee script there is no reason not to use it. A lot of thought has gone into it. But again private methods will be tricky. Usually private methods are simply made public but underscore prefixed Foo.prototype._privateMethod to denote their privateness. If that's not good enough, things start to get funky.

What if - for example - I would like to put private1 and public2 of Child into a prototype, so that these functions are referenced instead of copied?

Public methods can go in the prototype and propogate fine, private not so much. Private is not really a good way to think of things in javascript at all, as it's not supported like other languages. Instead, any value (including functions) that you have access to is either in your current scope via local variables or closure, or it's a property on an object that is in your current scope via local variables or closure.


EDIT:

Lastly, the pattern you have here would be better served without the new keyword. Since you are creating and returning an arbitrary object, you don't need the JS engine to create one for you. So your code should work if you simply remove the new entirely from your code here.

Constructor functions have a quirk. When they return an object they return that object instead of the one created via new. Here you are returning self which you made yourself via Object.create. If a constructor function returns a non-object (string, number, function, null) it will actually return this instead. In coffee script this can be tricky because of implicit returns form functions. And when using coffeescript classes, the constructors never return anything, allowing you to deal with this odd problem.

Upvotes: 1

Related Questions