Sign in to follow this  
Krohm

Not traversing the prototype chain?

Recommended Posts

Krohm    5030
I've come to the conclusion I missed something very important about the prototype chain and how to set up "proper" OOP inheritance in Javascript.
Here's what I think I'm doing:
01function Interesting() {
02    this.a = []; /* Array of Auxiliary structures */;
03    this.b = ...;
04}
05Interesting.prototype = {
06    Auxiliary: function(c, d, e, f) {
07        /* validity checks.
08        This is a basic "packet" of data.
09        It could be a stand-alone object but since it's used only
10        when dealing with Interesting objects, I decided to define
11        its ctor here. */
12    }
13};
14// ...
15Interesting.prototype.Auxiliary.prototype = {
16    c: undefined, // I'm still not sure about the style a base class should have BTW
17    d: undefined,
18    e: undefined,
19    f: undefined
20};
21// ...
22function DefaultInteresting() {
23    Interesting.call(this); // Seems to work ok.
24    this.parts[0] = new Interesting.Auxiliary(1, 2, 3, 4); // Auxiliary is undefined
25    this.b[0] = undefined;
26};
27DefaultInteresting.prototype = Interesting; // new Interesting() does not seem to work either

Problem is line 24. If I do new Interesting.prototype.Auxiliary(1, 2, 3, 4) then it calls the correct function.

Why is this happening? It was my understanding this should have been automatic. The inheritance chain in the debug window appears to be indeed correct. An hour of googling made me more confused.
What do you suggest me to look at?
 

Share this post


Link to post
Share on other sites
tanzanite7    1410
I have not slept in the last 28 hours, so ... i am bit dopy and not particularly helpful (and probably say some gibberish), but ...
 
I would throw away the "New" stuff and specify the prototype chain yourself (the "new" junk i think was added to the language to look more familiar for people coming from other programming languages - it's kinda hack. Having its own shadowy prototype chain which at best confused the bejesus out of anyone).
 
Some example:
var BaseObj = Object.create(null)  // new object with null as its prototype - clean object, no crap attached.
BaseObj.defaultA = 1 // some default value for BaseObj - everything will see it through the prototype chain.
BaseObj.defaultB = 42 // or you could treat it as a static variable by accessing it directly via BaseObj.defaultB instead of this.defaultB where "this" is always whatever object is bound ;)
BaseObj.build = function(a) { // no constructor for this object as it is just a base class
  this.a = a // will change (or add when it was missing) "a" to the bound "this" object (!!!)
  // ... which should never be BaseObj, unless you called BaseObj.build(123) in which case it obviously is exactly that
  // ... but you probably do not want (unless that "a" was supposed to be a "static" variable - to use the parlance from other languages)
  alert(this.defaultB) // obj = FunObj.make() will alert "2": obj does not have it defined, but FunObj in its prototype chain has - masking BaseObj
  // this.defaultB = 77 // this would not change BaseObj.defaultB, but add "defaultB" to "obj" (or whatever "this" is bound to)
}

var FunObj = Object.create(BaseObj) // new object with its prototype set to BaseObject
FunObj.defaultB = 2
FunObj.build = function(a, defA) {
  BaseObj.build.bind(this)(a) // FunObj has a build and will be found before
  // ... lets skip that and call parent builder explicitly .. a'la: "super(a)"
  this.defaultA = defA
}
FunObj.make = function() { // out custom constructor
  var obj = Object.create(FunObj)
  FunObj.build.bind(obj)(11, 22) // we want "this" to be "obj" while calling FunObj.build(11, 22)
  return obj
}

var snafu = FunObj.make()
// snafu.defaultA == 22
// snafu.defaultB == 2
// snafu.a = 11
// BaseObj and FunObj are unchanged, ie. BaseObj.defaultA == 1, BaseObj.defaultB == 42 etc
Much easier to understand what is going on. Ie. you really, really need to understand the, very-very simple, prototype style inheritance anyway to get anywhere with javascript - "new" is just a unneeded obstacle smudging the water for no benefit (well, at least i have not found one).

(hopefully i managed to be somewhat intelligible)
2 edits later:
(i need sleep, bye) Edited by tanzanite7

Share this post


Link to post
Share on other sites
tanzanite7    1410
"prototypal objects only"

???

That is all JavaScript has - there is nothing else really, just some wrapper syntax in the form of "new" / ".prototype" to confuse the matter.

Let's examine what "new" actually does:
function Bar() {}
function Foo() {}
// window.Foo is now an object of type function, its prototype is Function. Also, let's call the actual prototype of any object: "__proto__" to avoid confusion from now on.
// window.Foo.prototype == generic base object (constructor: "Function", __proto__: Object)
// IMPORTANT: "prototype" property here has nothing todo with an objects actual prototype (__proto__) ... lovely.
// "prototype" is kind of poor mans constructor.
 
Bar.prototype = new Foo;
// This line is equivalent to:
//   Bar.prototype = Object.create(Foo.prototype)
//   Foo.bind(Bar.prototype)()
That is all the magic "new"/"prototype" adds. Seriously. That is all it does.

A bit less typing (arguably) and a "prototype" property that has nothing todo with prototype (aka: __proto__). Forces you to inherit lots of unnecessary crap from the generic base object it uses. Is extremely limited on what one can do (the function object [a'la: Foo, Bar] serves as an FIXED SINGLE constructor). And in the end, none of it resembles any class based language (like Java etc) - ie, one still has to understand how the prototype chain works and program constructors etc accordingly - no help there from "new". Under the "new" wrapper - JavaScript is still JavaScript.

My suggestion: first understand how JavaScript actually works (ie. ignoring "new" and "prototype" property completely, __proto__ all the way) - then delve into "new", if at that point you see any point in doing so.

Share this post


Link to post
Share on other sites
tanzanite7    1410
Might as well comment on the OP now that my previous posts have covered the basics, somewhat.

Problem is line 24. If I do new Interesting.prototype.Auxiliary(1, 2, 3, 4) then it calls the correct function.

Why is this happening? It was my understanding this should have been automatic. The inheritance chain in the debug window appears to be indeed correct. An hour of googling made me more confused.
What do you suggest me to look at?

Your error is in thinking that "prototype" property has anything todo with prototype X_X *insert-rolleyes-etc*

"prototype" property is used behind the scenes to link up the real prototype chain via "new" as shown in previous post.

Btw, the correct way would be:
function Interesting ...
function Auxiliary ...
Auxiliary.prototype = new Interesting // (*)

obj = new Auxiliary // now obj.__proto__ == Auxiliary and obj.__proto__.__proto__ == Interesting

Which is not terribly useful, to say the least. Mostly because of how one is expected to use "constructors", see (*). The constructor is not a constructor for Instance - it is, in a sense, for Type! You need to construct the instance separately (logically, not necessarily in code ... if you can stand the headaches it will cause down the road) - a'la "build" in my first post.

Share this post


Link to post
Share on other sites
tanzanite7    1410

Then you are either misinterpreting what MDN or I say (or both). Care to elaborate? Maybe i can identify where the confusion lies.

 

This is fairly good roundup:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model

Highly "new" oriented, but sufficiently covers what i said (*), albeit with a lot of fluff - a hell of a lot of fluff. Have encountered a much more succinct roundup - but my Google-fu fails me atm.

 

(*) ie. what exactly "new" does, "prototype" vs "__proto__", how prototype chain works, inheritance pitfails.

Share this post


Link to post
Share on other sites
Krohm    5030

Note that in the article you mention (which is not new to me), when defining Manager extending Employee, they write

Manager.prototype = new Employee;

I don't think they are doing that because .prototype is irrelevant and in "determining instance relationships", the correlation between those two variables is further elaborated. While __proto__ appears in widespread use, I think I've read somewhere it was never meant to be exposed for real. Its doc page states indeed its use is deprecated.

 

It is worth noticing however they build the inheritance chain as

DefaultInteresting.prototype = new Interesting;

And not as I am doing, neither new Interesting() nor Interesting. This really makes sense on its own.

Share this post


Link to post
Share on other sites
tanzanite7    1410

I don't think they are doing that because .prototype is irrelevant

If it would be irrelevant then it would not exist at all - so, obviously it is used for something. It's only use is for "new" (as described earlier and in that article) - it has no other uses (well, exceptions excepted: instanceof vs isprototype). On its own - it does nothing (inc. not used for traversing prototype chain when reading object properties => ie. not the real prototype chain).
 

While __proto__ appears in widespread use, I think I've read somewhere it was never meant to be exposed for real. Its doc page states indeed its use is deprecated.

It indeed was not meant to be exposed - however, except IE they all do using "__proto__". Also, in future versions (ES6) it will be exposed as "__proto__" (not sure if the specification is finalized yet).

It is a more convenient alternative for the already standardized (hence the deprecation note) Object.setPrototypeOf/getPrototypeOf.

Its exposedness is not relevant to its existence tho (it is a fundamental part of the language). Referring to the property holding the real prototype chain using "__proto__" is just convenient (as virtually everything except IE exposes it with that name already).

"__proto__" = prototype chain
"prototype" = used to build the prototype chain for new objects created by "new"

... just got to love the confusion "new" managed to shoehorn into the language.
 

It is worth noticing however they build the inheritance chain as

DefaultInteresting.prototype = new Interesting;
And not as I am doing, neither new Interesting() nor Interesting. This really makes sense on its own.

Glad you are making progress on how to use "new" and its way of building the prototype chain. Just be sure you understand also what is going on under the hood.

Worth repeating in that regard perhaps:

1) Foo.prototype = SomeObject
2) Bar.prototype = new Foo
3) obj = new Bar

line 2: creates a new object and sets its "__proto__" to "SomeObject" and stores it in "Bar.prototype" (not in "Bar"'s prototype chain which is in "__proto__")

line 3: similarly creates a new object and sets its "__proto__" to the object stored in "Bar.prototype" (previously, line 2 stored an empty object with its "__proto__" set to "SomeObject" in there).

Ie. objects prototype, used by "new", is in "prototype" - with its real prototype chain pre built by line 2 and similar previous constructs.

Note that the property name "prototype" as an objects "prototype" makes sense - just that it is not the objects prototype CHAIN.

edit: well, just remembered that "instanceof" is related to "new" - can't think of anything else. Edited by tanzanite7

Share this post


Link to post
Share on other sites
Krohm    5030

I still don't understand the point you're trying to make here.

I suspect something is missing in translation.

 

The more I think at your "prototype is redundant" argument however, the more I think you are taking it the other way around.

Starting point: .__proto__ does not formally exist.

Inference: using .__proto__ is non-conforming.

Note: new is to be used to move value .prototype to implementation-specific management - ideally implementation-private data.

 

This still does not come to provide any hint on why using it does not influence the object prototype chain, as its value is copied at construction time, coherently with MDN documentation. The only thing you say is that .prototype does not take part  not object's prototype chain. This is incorrect, as you write yourself above. A more correct statement would be that current .prototype value is not considered during object prototype chain traversal, but this is irrelevant to my problem, which I suspect to be lingering in subtle line order issues.

 

As a side note, MDN isn't deprecating Object.setPrototypeOf(...) as you seem to suggest above - it marks .__proto__ as deprecated, coherently with a long history of documentation.

Last but not least, standardizing .__proto__ is just a terrible idea but of course nobody cares about that - see how let is supported!

Share this post


Link to post
Share on other sites
tanzanite7    1410

I still don't understand the point you're trying to make here.
I suspect something is missing in translation.

Quite possible. English is not my strong point and having half the posts written while sleep deprived probably did not help either.

A recap, from my limited point of view:

When i saw the OP the first time i could not even discern what was the goal - it made no sense. What was evident is that you, at that time, did not quite understand what "prototype" property does. Given that "pseudo-classical" way still needs one to understand the prototypal core of javascript - i suggested to forget "new" and go plain prototypal as a starting point (and then back to "new" if the will to do so prevails).

... and inevitable confusion ensued. Mostly around "prototype" property, as expected.

"prototype" = Object's prototype, vs "__proto__" next link in the prototype chain.

------------------------------
A slight tangent to explain where i come from.

When i first started with javascript (~10y ago) i used the "pseudo-classical" pattern as promoted by all tutorials/materials i encountered. It was very odd, coming from other languages that had real classes, but it works. And i was fine with it - till the intricacies and limitations of it started to bite me. I do not remember when or what exactly was the final trigger, but at some point i completely ditched "new" (greatly helped by the fact that i never had to care about IE, which at the time made a HUGE difference x_x).

I do not intend to say to not use "new" as it is in the language and does what it is supposed to - if it is also useful to you then use it.

While "new" ended up being too limiting/annoying to work with for me - it might never even make a difference for you. aka YMMV.
 

The more I think at your "prototype is redundant" argument however, the more I think you are taking it the other way around.
Starting point: .__proto__ does not formally exist.
Inference: using .__proto__ is non-conforming.
Note: new is to be used to move value .prototype to implementation-specific management - ideally implementation-private data.

/me confused. Not sure what you meant here. Some observational statements:
* "prototype is redundant":
No = in sense of: "new" and "instanceof" (anything else?) will use it, they can not work without.
Yes = in sense of: everything "new" provides can be done without it (strictly by the standard).
* ".__proto__" does not formally exist.
While strictly true (at this time), it is fundamentally irrelevant / wrong. Standardized "Object.get/setPrototypeOf" exist - which allows one to define a "__proto__" property (via standardized setters and getters) to behave indistinguishably to the real prototype chain propery. It exists and is exposed.
* "using .__proto__ is non-conforming"
It is a fundamental part of the language and must be understood. My uses of it so far have been for explanation purposes - there is no need to use it in code. But you can use it if you want (via "Object.*PrototypeOf" or "__proto__" with ES6).
 

This still does not come to provide any hint on why using it does not influence the object prototype chain, as its value is copied at construction time, coherently with MDN documentation. The only thing you say is that .prototype does not take part of(?) object's prototype chain. This is incorrect, as you write yourself above. A more correct statement would be that current .prototype value is not considered during object prototype chain traversal,


"does not take part of object's prototype chain" == "is not considered during object prototype chain traversal"

???

"This still does not come to provide any hint on why using it does not influence the object prototype chain"

I meant that changing / setting "prototype" does nothing (it is not the prototype chain) - only "new" / "instanceof" will ever use it ("new" specifically uses it to set the actual prototype chain link for the new object).
 

but this is irrelevant to my problem, which I suspect to be lingering in subtle line order issues.

I thought you solved it! What is the current, updated, problem?
 

As a side note, MDN isn't deprecating Object.setPrototypeOf(...) as you seem to suggest above - it marks .__proto__ as deprecated, coherently with a long history of documentation.

No, you mistook what i said. I meant to suggest that "__proto__" was marked as deprecated because the "Object.get/setPrototypeOf" came to be.
 

Last but not least, standardizing .__proto__ is just a terrible idea

Why?

While there is no need for it (for me at least) - it makes no sense to hide it either. It is just a property, just with special meaning attached to it. I would say that it should have been exposed from the very beginning. Glad they are coming to their senses finally.
 

but of course nobody cares about that - see how let is supported!

OT: Wow. I only vaguely remembered that it was related to scope in some way - had to google it up as i just could not remember what it exactly was. Found some interesting syntax options with it that i am not sure i ever knew (and with hindsight, did not miss either). While doing that, i come to think that i have never used it! Interesting nonetheless.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this