> VB6 does not autodeclare global variables when you forget to type "var".
Nor does JavaScript... "use strict";
Being what it is, JavaScript has come on a long way. Saying "avoid this specific feature" does make a language better. The same can be said about specific features of JavaScript (with, eval) but I'm sure you wouldn't make the same argument.
The fact seems to be that people are stuck in their ways and hate on JavaScript because they don't know the language as well they could and they haven't kept up to date with the evolving standards.
People who "hate on JavaScript" tend to know the language very well. They also know at least several other languages very well. This knowledge makes JavaScript's many flaws much more obvious, and much less justifiable.
The basic fact is that prototype-based OO, for example, is much less practical than class-based OO. Experienced developers comprehend JavaScript's attempt at it just fine. This understanding doesn't change its inferiority, however. This is exactly why so many developers need to compensate for JavaScript's lack of desired functionality in this area by trying to fake class-based OO using the limited functionality that JavaScript does offer.
The same goes for many other aspects of JavaScript. Bad tools are still bad, even in the hands of experts.
I'm not really sure what you're talking about when you mention "evolving standards". If anything, JavaScript's standards haven't evolved well at all. Harmony is only now proposing the addition of core functionality that has been present in other languages for decades now, and should have been in JavaScript from the very beginning, too. JavaScript is merely "evolving" to where it should have been many years ago.
To be honest, I am not so convinced about classes as units of program organization either. Classes conflate in a single mechanism several concepts:
1. Types (classes)
2. Encapsulation (private, protected)
3. Polymorphism (virtuals)
4. Code reuse (inheritance)
While it is much better than prototypal inheritance, it is still less than ideal. On the other hand, there are languages like Haskell and ML which compartmentalize better these concepts:
1. Types: algebraic data types and type synonyms
2. Encapsulation: modules
3. Polymorphism: type constructors (parametric), type classes (ad-hoc, Haskell only)
The result of providing these features independently from one another is a net increase in flexibility. You can have, say, a module that exports two or more types, and a single function inside that module that can manipulate the internals of both types.
===
Edit: And Rust gets this right as well, of course!
1. Types: structs and enums
2. Encapsulation: modules and crates
3. Polymorphism: generics (parametric) and traits (ad-hoc)
4. Code reuse: trait inheritance and trait instances for generic types
I'm extremely confused by this post. You claim to know JavaScript very well, which is fine, but then you go on to say that developers "fake class-based OO using the limited functionality that JavaScript does offer." That doesn't square with the fact that prototypal OO is strictly more expressive than class-based OO. You can implement class-based OO in a prototypal OO language. You cannot do the converse.
A lot of people who know JavaScript and several other languages very well don't hate on JavaScript. JavaScript has more than it's share of warts, obviously. We all know the story: it was written in a week, etc. It also has more than it's share of good parts, though, and if used correctly it is a very expressive language. In my experience, the haters are more frequently people who don't know the language as well. Clearly our experiences differ. I find it hard to believe, though, that anyone experienced in both languages would claim that JavaScript was worse than PHP, and PHP is still the most common back-end web language.
> That doesn't square with the fact that prototypal OO is strictly more expressive than class-based OO. You can implement class-based OO in a prototypal OO language. You cannot do the converse.
Actually, you can implement prototypal OO in a class-based language. First of all, you need a single class with two members variables and a member function. The member variables are a pointer or reference to a base object, and a hashmap from strings (member names) to objects (member values). In C++, this would be a std::unordered_map<std::string, boost::any>. The member function is an overload of the subscript operator (operator []) that takes a string argument and checks whether the hashmap contains the string as a key. If string is indeed a key, then its associated value is returned. Otherwise, the same subscript operator is evaluated for the base object and the same string. If there is no base object, then either an exception is thrown or a special undefined value is returned.
The feasibility of doing this is no big surprise. After all, dynamically typed languages are a very, very restricted subset of statically typed languages: http://existentialtype.wordpress.com/2011/03/19/dynamic-lang... . The main reason why users of class-based languages do not do this is that this breaks type safety for very little gain.
Actually, the supposed encoding of class-based OO in a prototypal language is not correct from an operational semantics point of view. In a statically typed OO language, methods and member variables must be known to exist, so they are directly used. The prototypal "encoding" fundamentally relies on testing at runtime whether methods or member variables exist, because it uses the mechanism described two paragraphs above. So it actually does something different than what statically typed class-based OO languages do.
> It also has more than it's share of good parts, though, and if used correctly it is a very expressive language.
For me, an expressive language is not a language that lets me encode hacks (after all, even C can do that), but a language that lets me encode mechanically checkable assurances that my code will work in specific ways. This allows other programmers to reuse my code with full confidence that it will work in the way they expect it to.
That's a pretty good try, but that does not implement the full flexibility of prototypal OO. Also, note that I wasn't talking about statically-typed class-based OO specifically. Classical OO exists in plenty of dynamic languages too, so your comments about type systems are kind of out of place. I prefer static typing too (as long as it comes with inference and polymorphism), but that's orthogonal to the discussion at hand.
> That's a pretty good try, but that does not implement the full flexibility of prototypal OO.
Are you talking about JavaScript's "this" keyword? I did not mention it in my preceding comment, because that is an implementation detail of JavaScript functions. I think my point still stands that (static) class-based OO languages are sufficiently powerful to encode the semantics of prototypal inheritance.
> Also, note that I wasn't talking about statically-typed class-based OO specifically. Classical OO exists in plenty of dynamic languages too, so your comments about type systems are kind of out of place.
You have a point there. When they say "classes", I usually think C++ classes or Eiffel classes, but you are right that there are dynamic languages that uses classes, too.
> that is an implementation detail of JavaScript functions.
Oh? So in an example where B has a method m, and A inherits from B, how is it that `this` refers to something different when calling B.m() than when calling A.m()? They're the exact same function.
That said, I do need to clarify. I was talking about implementing classical inheritance with prototypal inheritance, and implementing prototypal inheritance with classical inheritance. You can implement either using base language features if you want to create a new object system, but that's not what I was referring to.
> Oh? So in an example where B has a method m, and A inherits from B, how is it that `this` refers to something different when calling B.m() than when calling A.m()? They're the exact same function.
Just like in regular class-based OO languages, "this" is an implicit argument of every method that refers to the object on which the method was invoked.
You can even avoid manually doing all the "this" juggling yourself: Make a delegate class, which holds a reference to a method (including its captured variables) and a reference to the "this" object. When a method is retrieved from a an object, actually construct a delegate referencing both the method and the object. (If the method is retrieved after traversing the inheritance chain "upwards", fix the "this" reference along the way "downwards".) Finally, when a delegate is assigned to a variable or as a member of another object, get rid of the "this" reference and assign the method only instead. Client code never gets to see the "this" juggling.
Oh I know how to implement it, I was just responding to your claim that it was "an implementation detail of JavaScript functions." It's an implementation detail of method calls, the function itself is completely this-agnostic. I guess I was just being pedantic ;)
Obviously not. I do, of course, disagree with your reduction of prototypal OO to "looking up missing keys in another map".
That said, I do need to clarify. I was talking about implementing classical inheritance with prototypal inheritance, and implementing prototypal inheritance with classical inheritance. You can implement either using base language features if you want to create a new object system, but that's not what I was referring to.
Well, it would help my understanding if you could elaborate on your disagreement rather than just saying that you disagree.
The additional constraint of not creating a new object system prevents me from making OO of any kind at all in C, but leaves most scripting languages that implement "classical inheritance" very open to implementing "prototypal inheritance". This is partly because they actually use "prototypal inheritance" and expose a "classical interface" as the primary one, so fiddling with __mro__ feels a little like cheating. If I want to avoid fiddling with the existing __mro__ and build my own, I can override __getattribute__. This certainly doesn't feel like making a new object system to me, but it also doesn't feel substantially different from overriding [] in C++.
To be honest, I have no idea how to implement the exact operational semantics of prototypal inheritance in a dynamic language with classes. (Perhaps it is possible, but I would not bet on it.) In JavaScript, objects are statically known to be internally a pair of a base reference and a hashmap. In Python, you can define a tuple or class that has a base reference and a hashmap as members, but internally your program will test for the existence of these members all the time. So, different semantics.
But a static OO language can faithfully reproduce the operational semantics of prototypal inheritance.
The difference between "checking for the existence of the proto key in a hash map" and "checking whether the proto pointer that I am certain to have points to NULL" is due entirely to the differences in the semantics of Python and C++. Is it important just to implement prototypes, or to ensure that the guts of the prototype system are the same as the guts of a particular JS implementation? I'm not sure precisely where the goalposts have gone, so here are some prototypes in Python. http://pastie.org/8263966
Edit: Now with method binding that works instead of not working!
Nor does JavaScript... "use strict";
Being what it is, JavaScript has come on a long way. Saying "avoid this specific feature" does make a language better. The same can be said about specific features of JavaScript (with, eval) but I'm sure you wouldn't make the same argument.
The fact seems to be that people are stuck in their ways and hate on JavaScript because they don't know the language as well they could and they haven't kept up to date with the evolving standards.