5 ms·
I think I will never understand Javascript prototype chain. People always say that it's actually simpler than "traditional", Java-like classes... yet when JS g
by shp0ngle 5y ago
I think I will never understand Javascript prototype chain.
People always say that it's actually simpler than "traditional", Java-like classes... yet when JS gained that, with ES6, I could immediately "grok" what `class` and `new` was doing. I could never do that with __proto__ and prototype.
- klodolph 5y agoI think it’s simpler from an implementation standpoint, that’s the only simplicity it brings to the table. Among other things, it does not require any new syntax.
- masklinn 5y ago> Among other things, it does not require any new syntax. Which was added anyway, because Javascript got constructors (and `new`) to match Java,
- egeozcan 5y agoIt depends on your starting point. Java-like classes are simpler to you because you already know them. JS prototypes have a few simple rules you also need to know. I personally think both are simple.
- shp0ngle 5y agoIt might be that. As I use Java-like classes everywhere; and the moment they were possible in JS through Babel, I jumped on that too.
- inbx0 5y agoAre you using some bespoke Babel plugin that actually adds Java-like classical inheritance to JavaScript, or are you implying that the standard EcmaScript 6 classes which Babel handles by default are somehow "Java-like" instead of prototype based? Because they are not, they still use the same prototype-based inheritance, even though the syntax kinda looks like Java, and have all the same pros and cons of prototypal inheritance as before.
- shp0ngle 5y agoI am now using standard ES6 without Babel. I kind of know what `class`, `new` and `extends` does, I never know what `__proto__` and `prototype` does without googling it and staring at the code for a long time. So maybe I am wrong the whole time!
- nosianu 5y agoThe point is you don't need to know though. It's called __proto__ exactly because it's something internal that the programmer of course can have some fun with if they so choose, but it is unnecessary for using it. Unless you do extreme meta- stuff, but that is very far from normal programming and should be done by few and very sparingly and well-targeted and triple-checked. I mean, for the advanced programmer knowing things about internal workings of the ECMAScript spec and of actual runtimes - most such bog posts are about V8, covering Chrome, Edge (new), node.js - are good to know, such as what is an Execution Context, an Execution Stack, and what is a Scope Chain and how are they implemented. Or, what happens internally when you modify an object's structure by adding or removing properties. Or, what happens when you call a function with different types and numbers of parameters, how /why this destroys internal optimizations. Most programmers will have only a fuzzy, if any, understanding of those though, and that's fine. Even before "class" was introduced into Javascript people were constructing "classes" left and right, using the prototype, and many (most?) did not know of or did not use __proto__. The latter is used internally during runtime, it is not something you need to manipulate. Your "interface" is the `prototype` property (see https://stackoverflow.com/a/9959753/544779 https://stackoverflow.com/a/9959753/544779). Reading the ECMAScript spec for someone thus far only well-versed in MDN as Javascript documentation feels quite weird, there's some learning curve required, new words, new concepts. But that's because now you opened the hood and are looking at the gears and the engine of the language. The basics of some event loop internals, such as what microtasks are, are much more important to know. Asynchronous issues catch people more often and are much harder to debug.
- BigJono 5y agoWhen people talk about something being simple (or complex), they're talking about how many "things" there are in the concept, rather than how easy or intuitive it is to understand (since that's subjective, as pointed out). Prototypal inheritence is seen as simpler because conceptually it involves less "things". Both JS and Java have runtimes with values containing basic data (strings, ints, bools etc) as well as objects. Where they differ is that JS has first class functions (meaning functions are also values just like basic data and objects), which is what enables prototypal inheritance. And Java has a compile time where it generates static objects (classes) that aren't values and can't be changed, that's what enables classical inheritance. It's generally considered that conceptually the functional approach is simpler, because in those languages functions adhere to the same rules as objects and other data, you can add new ones, remove them, move them around, copy them into a different variable etc. So all the "things" that apply to working with functions are just the same "things" that work on everything else, there's nothing really extra there, you don't need all these special rules and concepts like "reflection" where you're "reaching into the internals" of the running program or whatever it is in Java, because you don't have any "internals" to reach into. Nothing is happening at some compile time before the code actually runs, and nothing is static or out of bounds. None of that really has much to do with prototypal inheritance itself. But it's relevant in that you can't just pick which kind of inheritance to use, you need the language constructs that enable them, so they're part of the complexity. The two forms of inheritance are basically the same thing, you just have an object with your functions on it that has a special label saying "if you don't find what you're looking for, look at this object instead". The difference is that in Javascript that label is just a standard property on an object, that can hold another object. Whereas in Java, that label is an "extends" or "inherits" clause on class that's wired up at compile time, and at runtime it's treated as static and outside of the programmer's control.
- kaba0 5y agoJust a nitpick, but Java’s classes are values (though immutable) and are available by the getClass method of every object. But thank you, great summary! Though now I am interested in whether a javascript-like inheritance is pretty much possible with lambdas in java, since instance field resolution does work identically to the javascript logic (though with runtime immutable inheritance chain).
- mhitza 5y agoThe prototype object model is half-baked in JavaScript. The other prototype based language I've programmed in was the Io language and there it made sense, just by having a built-in clone utility. See Objects section https://iolanguage.org/tutorial.html https://iolanguage.org/tutorial.html With the lack of a clone operation, in order to define new objects types you had to go through hacky stuff, like explicitly setting up .prototype properties.
- masklinn 5y ago> The prototype object model is half-baked in JavaScript. Exactly. Not just half-baked but half-assed too, the entire concept of constructors doesn't belong for starters. Javascript's prototypal system was never a philosophy, it was just a way to quickly get an object system up and running (which is why delegation has to be hand-rolled, delegating ctors stinks, and most of the native objects can't be derived from at all), essentially a terrible implementation detail people had to deal with out of necessity. > With the lack of a clone operation, in order to define new objects types you had to go through hacky stuff, like explicitly setting up .prototype properties. As you link to io, JS IO's clone way back (in ES5 I think?) in Object.create. I don't think it's very good, though I think IO's clone is even worse as it really do what you'd intuit in reading the word (I think JS's naming is less confusing, though hardly great). In Self, the "clone" operation would simply do a shallow copy (unless you'd overridden it). So you'd have an object, "clone" it, and you'd get a copy of that with the same slots set to the same value. A prototype then would simply be an existing "reference" instance you'd copy, and you'd get the same parent slots, meaning you'd get the same parents (what Javascript and Io mis-call prototypes).
- eurasiantiger 5y agoValid critique in legacy sense, the class syntax of course mitigates much of it. But why would you want to delegate constructors?
- masklinn 5y ago> But why would you want to delegate constructors? Because if you inherit from something you probably want to run the super object's constructor so that yours is fully initialised?
- Jasper_ 5y agoAn object 'a' having a prototype of 'b' means that if a property is not found on 'a', it will then look it up on 'b'. It's just chaining. To set an object's prototype, you set the special __proto__ property on it. var a = { foo: 5 }; a.__proto__ = { foo: 10, bar: 2 }; console.log(a.foo, a.bar); // 5, 2 You can also use Object.create() to initialize an object with a given prototype. So the above is equivalent to: var a = Object.create({ foo: 10, bar: 2 }); a.foo = 5; console.log(a.foo, a.bar); // 5, 2 The real confusing part is the property called "prototype". This doesn't exist on random objects, it's only available on functions, and it sets the __proto__ of the object created with that function when used as a constructor (that is, when using the "new" operator). function Foo() {} Foo.prototype = { foo: 10, bar: 2 }; var a = new Foo(); a.foo = 5; console.log(a.foo, a.bar); // 5, 2 The "new" operator also sets a.constructor = Foo, and calls the constructor method. You can think of the new operator as syntax sugar performing the below: function Foo() {} Foo.prototype = { foo: 10, bar: 2 }; // The four lines below are equivalent to `var a = new Foo();` var a = {}; a.constructor = Foo; a.__proto__ = Foo.prototype; Foo.call(a); a.foo = 5; console.log(a.foo, a.bar); // 5, 2 The reason for all of these different mechanisms is history. The original mechanism was the constructor `prototype`, then in ES5, Object.create() was added. Then, much later on, `__proto__` was standardized (it was a non-standard extension before).
- brianzelip 5y agofunction Foo() {} Foo.prototype = { foo: 10, bar: 2 }; var a = new Foo(); console.log(a.foo, a.bar); // 5, 2 Isn’t your logged a.foo wrong? a.foo === 10, right?
- Jasper_ 5y agoOops, I forgot to add the `a.foo = 5;` line like I had in the other examples. Fixed.
- deleted 5y ago[deleted]
- jraph 5y ago> yet when JS gained that, with ES6, I could immediately "grok" what `class` and `new` was doing. I could never do that with __proto__ and prototype. The new ES6 "class" syntax is really syntax sugar on the existing prototype system. "class" does not add new functionality to the language. Anything that can be written with "class" can be written with the older way. So really, you need to understand the prototype system to really understand how "class" works. But I think you don't actually need to deeply understand how the prototype system works to use the new ES6 class syntax. Your "Java intuition" + the knowing that there could be gotchas in corner cases should suffice if you don't do anything weird.