10 biggest JS pitfalls
From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Axel Rauschmayer Sent: Sunday, December 30, 2012 16:22
9c) using methods as callback functions
The single biggest feature on my ES7 wishlist is the bind operator strawman:
Far ahead of Object.observe or value objects, this alone would make my day-to-day code much simpler.
As-is I stick with the closure pattern for my classes because (a) I get actual encapsulation; and (b) my methods are "bound" without awkward obj.method.bind(obj)
—or worse, in ES3 environments: _.bind(obj.method, obj)
. Compilers hate me for it, and punish me with worse-performing and memory-duplicating code (vs. the prototype pattern), but for now it's been a tradeoff I'm willing to make. Symbols in ES6 fix (a), but we need the bind operator to fix (b).
I'd say String.replace needs to be there: blog.mindedsecurity.com/2010/09/twitter-domxss-wrong-fix-and-something.html, www.thespanner.co.uk/2010/09/27/string-replace-javascript-bad-design
Also the fact that no built in html encode/decode exists.
You did not include variants of
var i, a=[]; for (i=0; i < 10; i++) { a.push(print(i)); } print(a[3]); /* output is 9 */
in your list. I see related bugs on a regular basis.
The other place I see regular bugs by intermediate coders is related to 'this' in events; but I'm not sure if we can count that as a JS problem or a DOM bug (Is that 9c in your list?) --
function A() { this.a = 123; } A.prototype.p = function {alert(this.a)}; window.onclick = new A().p;
IIRC, clicking will alert undefined, as the event handler is invoked with this as the event. IME, this is not what programmers mean when they write that.
We can work around it by changing the constructor: function A() { this.a = 123; this.p = this.p.bind(this); }
but of course now we are over-allocating for no good reason IMO. Very frustrating.
.#3 on your list is entertaining, I have been told that == is faster than === in at least one implementation, because that's what sunspider tests.
On Sun, Dec 30, 2012 at 10:22 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
- Array-like objects [not completely fixed (DOM...), but
arguments
Specifics welcome, though probably better posted to www-dom at w3.org. We've made some effort towards changing some of this, but legacy content is against us. :/
Axel Rauschmayer wrote:
- Both undefined and null [not too much of a problem, easily learned]
- Truthy and falsy values [not pretty, but I’ve grown used to them and the convenient but sloppy “has a value” checks via
if
]
These are usually winning. Dart makes you be explicit in testing and convering, it's a pain from what I can tell.
ToBoolean implicit conversions usually DWIM. The ToNumber, ToInt32, and ToUint32 conversions more often go wrong.
- == [easy fix: always use ===]
- Parameter handling [fixed in ES6]
- Array-like objects [not completely fixed (DOM...), but
arguments
becomes obsolete in ES6]- Function-scoped variables [
let
FTW in ES6]- Accidental sharing of data [for-of will help in ES6]
- Creating sub-constructors is messy [fixed via classes and
super
in ES6]this
in non-method functions: 9a) Referring to thethis
of a surrounding method, 9b) accidentally creating globals by forgettingnew
, 9c) using methods as callback functions [(a) and (b) fixed by ES6 arrow functions and ES5 strict mode]- The for-in loop [simple rule: avoid if you can, already possible in ES5]
Thus: 1-3 won’t go away soon. 4-10 are mostly eliminated by ES6.
Deliberate omissions:
- Implicit conversions are messy (and a superset of pitfall #2), but seem to surprise people much less than the above items.
I think you have it backwards.
- Modules close another important hole in JavaScript, but I wouldn’t consider that hole a pitfall.
You mean free variables being static errors? That is not a done deal yet.
Missing properties yielding undefined still burns many JS programmers. Not an implicit conversion, so worth a separate item.
You did not include variants of
var i, a=[]; for (i=0; i < 10; i++) { a.push(print(i)); } print(a[3]); /* output is 9 */
in your list. I see related bugs on a regular basis.
That would be (7). The above should work. The problem only exists with functions (or rather, closures): var result = []; for (var i=0; i < 5; i++) { result.push(function () { return i }); } console.log(result3); // 5 (not 3!) Code for adding event handlers via a loop is often similar. Either for-of or Array.prototype.forEach() help.
The other place I see regular bugs by intermediate coders is related to 'this' in events; but I'm not sure if we can count that as a JS problem or a DOM bug (Is that 9c in your list?) --
function A() { this.a = 123; } A.prototype.p = function {alert(this.a)}; window.onclick = new A().p;
IIRC, clicking will alert undefined, as the event handler is invoked with this as the event. IME, this is not what programmers mean when they write that.
We can work around it by changing the constructor: function A() { this.a = 123; this.p = this.p.bind(this); }
but of course now we are over-allocating for no good reason IMO. Very frustrating.
Yes, that’s (9c). I would love for JS to make the distinction between reading a property and invoking a function. Then one could automatically bind if a method is read. But that doesn’t seem to be in the cards.
#3 on your list is entertaining, I have been told that == is faster than === in at least one implementation, because that's what sunspider tests.
It seems that strict equals is usually faster.
Yes, tripped me up a few times. Then I remember to use a regular expression with a /g flag as the first argument. I wouldn’t consider it a major pitfall, but it is definitely a pitfall.
Axel Rauschmayer wrote:
You did not include variants of
var i, a=[]; for (i=0; i < 10; i++) { a.push(print(i)); } print(a[3]); /* output is 9 */
in your list. I see related bugs on a regular basis.
That would be (7). The above should work. The problem only exists with functions (or rather, closures): var result = []; for (var i=0; i < 5; i++) { result.push(function () { return i }); } console.log(result3); // 5 (not 3!) Code for adding event handlers via a loop is often similar. Either for-of or Array.prototype.forEach() help.
You don't mean for-of, you mean for (let i...) in any form. We want all for-let forms to make fresh let bindings per iteration. That's what your resolution for (7) should say, and ES6 does indeed aim to do this.
We can work around it by changing the constructor: function A() { this.a = 123; this.p = this.p.bind(this); }
but of course now we are over-allocating for no good reason IMO.
Very frustrating.Yes, that’s (9c). I would love for JS to make the distinction between reading a property and invoking a function.
If you "fix" this by making extraction of a function value from a property auto-bind, which will have the same overhead under the hood that Wes cites in his explicit bind-based version.
If you let the function be extracted with unbound |this|, then you'll have bugs where it is called on the wrong |this| (undefined, let's hope).
You can't have it both ways. In particular, auto-bind on extract costs (even moreso if there's a memo under the hood to reuse the same single bound method -- which is mutable so would make a side channel).
JS is functional first, not OOP first. APIs that want methods to be extractable as bound to the object from which they were extracted must do extra work, which can be self-hosted via getters for specific methods, or a general proxy.
Then one could automatically bind if a method is read. But that doesn’t seem to be in the cards.
Wes cited the cost of the allocation in the constructor, but it can be deferred without requiring the language to automate binding (which would seem to require new special forms for declaring "methods").
If the allocation on construct is the issue, use the tools at hand to fix it, I say. Wes?
#3 on your list is entertaining, I have been told that == is faster than === in at least one implementation, because that's what sunspider tests.
It seems that strict equals is usually faster.
Should be, but they'll probably end up the same in any hot code that cares, in all optimizing VMs in 2014. :
- Implicit conversions are messy (and a superset of pitfall #2), but seem to surprise people much less than the above items.
I think you have it backwards.
My experience, so far. I’d be interested in other examples of implicit conversions stumping people.
- Modules close another important hole in JavaScript, but I wouldn’t consider that hole a pitfall.
You mean free variables being static errors? That is not a done deal yet.
No, I meant the hole of not having a module system that is part of the language. They don’t plug, they are the plug. ;-) Sorry for being ambiguous.
Missing properties yielding undefined still burns many JS programmers. Not an implicit conversion, so worth a separate item.
In chains? obj.prop1.prop2?
You don't mean for-of, you mean for (let i...) in any form. We want all for-let forms to make fresh let bindings per iteration. That's what your resolution for (7) should say, and ES6 does indeed aim to do this.
Yes, thanks. I thought only for-of did this.
We can work around it by changing the constructor: function A() { this.a = 123; this.p = this.p.bind(this); }
but of course now we are over-allocating for no good reason IMO. Very frustrating.
Yes, that’s (9c). I would love for JS to make the distinction between reading a property and invoking a function.
If you "fix" this by making extraction of a function value from a property auto-bind, which will have the same overhead under the hood that Wes cites in his explicit bind-based version.
If you let the function be extracted with unbound |this|, then you'll have bugs where it is called on the wrong |this| (undefined, let's hope).
You can't have it both ways. In particular, auto-bind on extract costs (even moreso if there's a memo under the hood to reuse the same single bound method -- which is mutable so would make a side channel).
JS is functional first, not OOP first. APIs that want methods to be extractable as bound to the object from which they were extracted must do extra work, which can be self-hosted via getters for specific methods, or a general proxy.
Again, not sure if that’s feasible, but it would help if one could make the distinction between a property being read (“get”) and a method being invoked (“call”).
{ function extractableMethod(...) { ... } Object.defineProperty(Foo.prototype, 'extractableMethod', { get() { return extractableMethod.bind(Foo.prototype); }, call(...args) { return extractableMethod.call(Foo.prototype, ...args); } }); }
Still messy and probably not backwards compatible, so a bind operator might be a better idea, after all: elem.onclick = obj::foo; // or the proposed ::obj.foo instead of elem.onclick = obj.foo.bind(obj);
Axel Rauschmayer wrote:
- Implicit conversions are messy (and a superset of pitfall #2), but seem to surprise people much less than the above items.
I think you have it backwards.
My experience, so far. I’d be interested in other examples of implicit conversions stumping people.
What stumped who, when? Any bugs/github issues/blog posts to cite?
if (!p) { /* p is null or 0 in C */ }
is common in C-like languages that tolerate it. JS does, with a Perl/AWK/etc. addition: "" is falsy, as well as 0, null, undefined, and false.
I've never heard anyone doing real work complain, and the Object Detection pattern on the web relies on undefined => false along with
missing property read resulting in undefined.
- Modules close another important hole in JavaScript, but I wouldn’t consider that hole a pitfall.
You mean free variables being static errors? That is not a done deal yet.
No, I meant the hole of not having a module system that is part of the language. They don’t plug, they are the plug. ;-) Sorry for being ambiguous.
But what's the pitfall? "Lack of a module system" describes a solution more than a symptom.
Missing properties yielding undefined still burns many JS programmers. Not an implicit conversion, so worth a separate item.
In chains? obj.prop1.prop2?
No, that throws promptly with near-enough blame!
The problem is the dual of object detection as a useful pattern. When you expect var foo = obj.foo to get an extant property's value, and let foo flow off into complex control flows before being dereferenced, then you have the "come-from" problem.
Axel Rauschmayer wrote:
JS is functional first, not OOP first. APIs that want methods to be extractable as bound to the object from which they were extracted must do extra work, which can be self-hosted via getters for specific methods, or a general proxy.
Again, not sure if that’s feasible, but it would help if one could make the distinction between a property being read (“get”) and a method being invoked (“call”).
{ function extractableMethod(...) { ... } Object.defineProperty(Foo.prototype, 'extractableMethod', { get() { return extractableMethod.bind(Foo.prototype); }, call(...args) { return extractableMethod.call(Foo.prototype, ...args); } }); }
Good point, the getter would bind even for calls that don't need it. Proxies don't help because their design eschewed an 'invoke' trap in favor of minimalism and JS high-fidelity.
So I'm wrong, and this does up the ante for a built-in solution, the bind operator.
Still messy and probably not backwards compatible, so a bind operator might be a better idea, after all: elem.onclick = obj::foo; // or the proposed ::obj.foo instead of elem.onclick = obj.foo.bind(obj);
Yes, this is Dave's proposal (the ::obj.foo syntax is better in my view and it's there, just not in the grammar sketch):
I’d be interested in other examples of implicit conversions stumping people.
What stumped who, when? Any bugs/github issues/blog posts to cite?
No, I think I agree with your assessment that implicit conversion to boolean (the most prevalent implicit conversion) is manageable, possibly even desirable.
I was interested in citations regarding non-bolean implicit conversions. So far, my impression has been that other implicit conversions either worked as people expected (+) or were never triggered (-).
- Modules close another important hole in JavaScript, but I wouldn’t consider that hole a pitfall.
You mean free variables being static errors? That is not a done deal yet.
No, I meant the hole of not having a module system that is part of the language. They don’t plug, they are the plug. ;-) Sorry for being ambiguous.
But what's the pitfall? "Lack of a module system" describes a solution more than a symptom.
“I wouldn’t consider that hole a pitfall.” (first quoted line above)
I consider not having modules built into the language as a missing piece, a hole. Are you disagreeing with that assertion? Or with my way of putting things?
But even if you disagree then ES6 modules still have two advantages (as opposed to, say, adopting AMD as part of ES6):
- We’fll have a common standard (that probably wouldn’t happen otherwise).
- Easier to analyse statically: calculist.org/blog/2012/06/29/static-module-resolution
Missing properties yielding undefined still burns many JS programmers. Not an implicit conversion, so worth a separate item.
In chains? obj.prop1.prop2?
No, that throws promptly with near-enough blame!
The problem is the dual of object detection as a useful pattern. When you expect var foo = obj.foo to get an extant property's value, and let foo flow off into complex control flows before being dereferenced, then you have the "come-from" problem.
Right. Automatically creating properties (typos...) and the “assignment vs. definition” issue are relevant here, too.
I'm surprised not to see Automatic Semicolon Insertion in the list.
Juan
Le 31 déc. 2012 à 10:17, Axel Rauschmayer <axel at rauschma.de> a écrit :
I’d be interested in other examples of implicit conversions stumping people.
What stumped who, when? Any bugs/github issues/blog posts to cite?
No, I think I agree with your assessment that implicit conversion to boolean (the most prevalent implicit conversion) is manageable, possibly even desirable.
I was interested in citations regarding non-bolean implicit conversions. So far, my impression has been that other implicit conversions either worked as people expected (+) or were never triggered (-).
Personally, I have issues with from- and to-String conversions when working with HTML form field values and data-* attributes. More precisely, I am careful to make these conversions manually, and I regret that JS doesn't warn me if I forget to do them. Indeed the following cases are problematic:
- String-to-Number : not triggered in, e.g., x + '1' when x is a Number.
- Boolean-to-String, Null-to-String: false and null are not converted to a falsy string.
The only would-be issue with to-Boolean conversion, is that an empty array is not falsy as in Perl or PHP, and I have to remind to be a little more lengthy (pun intended) to test empty arrays. Except that case, to-Boolean implicit conversion is not only "manageable" and "possibly even desirable" : it works without hiccup and is positively useful.
On 31/12/2012, at 15:55, Juan Ignacio Dopazo wrote:
I'm surprised not to see Automatic Semicolon Insertion in the list.
Yes I would ditch ASI altogether if only to force the javascrhipsters to put back each and every semicolon where it belongs: they are delimiters.
No ASI would force them to stop writing in those silly -and ugly- dialects in which every now and then lines begin with a semicolon...
As JS compiler wants semicolons as delimiters, instead of attempting to "guess" and "fix" buggy src code via A.S.I. which often results in failure anyway (even silent failures which is worse), it should better halt and complain loudly about syntax errors.
IOW, Javascrhipster's style code is nothing but a big multi line syntax error, fixed by ASI.
Happy new year!
Personally, I have issues with from- and to-String conversions when working with HTML form field values and data-* attributes. More precisely, I am careful to make these conversions manually, and I regret that JS doesn't warn me if I forget to do them. Indeed the following cases are problematic:
- String-to-Number : not triggered in, e.g., x + '1' when x is a Number.
I would assume the opposite being a problem: The user enters the number '33' in a form field, you try to add 1, but forget that it is a string and end up with '331' instead of 34.
- Boolean-to-String, Null-to-String: false and null are not converted to a falsy string.
When does this happen? Can you explain?
Thanks!
Axel
Le 31 déc. 2012 à 18:58, Axel Rauschmayer <axel at rauschma.de> a écrit :
Personally, I have issues with from- and to-String conversions when working with HTML form field values and data-* attributes. More precisely, I am careful to make these conversions manually, and I regret that JS doesn't warn me if I forget to do them. Indeed the following cases are problematic:
- String-to-Number : not triggered in, e.g., x + '1' when x is a Number.
I would assume the opposite being a problem: The user enters the number '33' in a form field, you try to add 1, but forget that it is a string and end up with '331' instead of 34.
It is what I meant: (but I should have written: x + myFormField.value to be clearer).
- Boolean-to-String, Null-to-String: false and null are not converted to a falsy string.
When does this happen? Can you explain?
When you store «false» in a HTML data-* attribute, and retrieve it later from there, you don't get a falsy value if you had naively relied on the implicit conversions. Or, when you put a boolean in a hidden form field and expect it to be treated as truthy/falsy server-side by the program receiving the form.
I don't think ASI per se is the problem. Rather, the restricted productions bite people:
function foo() { return very + long + expression() + goes + here; }
and of course, the expectation of ASI where there's no error to correct:
a = b (function () { ... // lots of lines })();
You could mean these by ASI, but the solution some people want is not mandatory semicolons, because that does nothing to help the second example above. Rather, we'd need more newline significance. That is where I want to take
but after ES6 is more in hand.
Jorge Chamorro wrote:
On 31/12/2012, at 15:55, Juan Ignacio Dopazo wrote:
I'm surprised not to see Automatic Semicolon Insertion in the list.
Yes I would ditch ASI altogether if only to force the javascrhipsters to put back each and every semicolon where it belongs: they are delimiters.
Doesn't help this case:
a = b (function () { ... // lots of lines })();
because it's easy to leave out a semicolon after b, even if you try to practice MSI (Manual...) and hate ASI. I've seen this mistake made by ardent MSI practitioners.
I've just discovered that someone is fallen in the following pit: Change of the semantic of function declarations in Firefox when included in block (spoiler: hoisting issue). (Not standard ES for sure, but definitely JS.) Enjoy: statichtml.com/2011/spidermonkey-function-hoisting.html
Yes, see the thread starting at
esdiscuss/2012-December/027419
if you haven't.
The SpiderMonkey implementation was "first", for what it's worth -- from 1997. ES3 did not specify any function in block production, of course.
The intersection with IE semantics is such that it's safe to use the function's name only later in the same block, but for some "then" blocks where the condition happens to be always true, and a few others where there's an outer definition of the same name -- the "if" condition is object-detecting using typeof foo == "undefined", e.g., for wanted function foo, then the function's name could be used after the "then" block without error.
We may be able to evangelize, since as you discovered, people have noticed the lack of interop already (that blog post is from 2011), so there may not be too many sites depending on intersection semantics of existing engines but not ES6 block-scoped (but not hoisted -- use after declaration) semantics.
I'm still amazed to see people rant about other people's perfectly fine coding styles, when those are an overtly subjective matter, and therefore no argumentation on whether "Yes ASI" or "Nay ASI" can be right.
The only facts about ASI are:
1.) It's a syntax error; 2.) Such syntax error is spec'd to be "fixed" in a deterministic process — no "guessing" games here from a grammar POV; 3.) No absolute consensus on whether it's a good or bad feature exists;
On the other hand, Python and Haskell programmers seem to be fairly happy with ASI in their languages. I, despite being a Pythonista, actually prefer ECMAScript ASI rules to Python ones, but as Brendan said, more newline significance would "solve" part of the "problem" — as in, we'd have rules a bit closer to what Python has.
As for the list, I'd rather put the implicit errors that might go unnoticed due to scoping/binding warts up there. Clearly calling a function without defining an object should have |this| as Null (fixed) — and it aligns more with the "Functional First" thingie —, and assigning to undeclared vars in the current or upper-scopes should throw an error rather than create a new property in the global object.
Exactly Brendan, I could not agree more and this is my No. 1 pitfall about JS: developers often not doing real work complaining about stuff that developers doing real work don't even care about or never ever had to worry about.
In any case they can learn and understand the feature/problem using the feature when needed, avoiding its weakness when necessary.
About falsy and truthy, null and undefined, who cares ... seriously, and to be honest, that's not a pitfall, is rather a feature when needed as it is for all other scripting languages as long as you know what you are doing ... and no programming language will save you if you don't know what you are doing and it's your duty, as developer, to understand the language you are using if that's your job.
Again, about falsy ... if I see a glass empty, it does not mean I used a microscope to understand no water is left in the whole glass surface ... I just consider that empty and I add water on top.
Engineers have the tendency to over complicate even simple tasks as the one I've just described ... what is the benefit? What is the result? That the day falsy values in JS will disappear libraries authors will implement an isFalsy(value) function/method and use it 90% of the time regretting the trick with == disappeared ... isn't it ;-)
Last, but not least, happy new year to everyone :-)
br
:-) Well put Andrea.
Benoit
Exactly Brendan, I could not agree more and this is my No. 1 pitfall about JS: developers often not doing real work complaining about stuff that developers doing real work don't even care about or never ever had to worry about.
I don’t follow. Who are these people not doing “real work”? And I don’t think discussing the language qualifies as complaining.
In any case they can learn and understand the feature/problem using the feature when needed, avoiding its weakness when necessary.
About falsy and truthy, null and undefined, who cares ... seriously, and to be honest, that's not a pitfall, is rather a feature when needed as it is for all other scripting languages as long as you know what you are doing ... and no programming language will save you if you don't know what you are doing and it's your duty, as developer, to understand the language you are using if that's your job.
“Warts” is probably a better term than “pitfalls”.
Again, about falsy ... if I see a glass empty, it does not mean I used a microscope to understand no water is left in the whole glass surface ... I just consider that empty and I add water on top.
Engineers have the tendency to over complicate even simple tasks as the one I've just described ... what is the benefit? What is the result? That the day falsy values in JS will disappear libraries authors will implement an isFalsy(value) function/method and use it 90% of the time regretting the trick with == disappeared ... isn't it ;-)
What is the trick with ==? Note that == does not respect truthiness or falsiness:
> 2 == true
false
> 2 == false
false
> '2' == true
false
> '2' == false
false
Axel, it wasn't referred to you explicitly, rather to all those developers still wining about this == null problem which has never been in my daily, real work, code.
choose one: value === undefined || value === null
in a language where undefined and null are different and for a good reason (not defined is different from defined as null) that one, against
value == null
who wins ... and why, if specs say latter one is equivalent? because!
Same is for falsy, I don't even want to start talking about it ... you write JavaScript, learn these two or three things that could be mistaken, learn how take advantage and avoid mistaken them when needed.
Do not think what you learned at University about Java is the way every programming language should be .... do not think other programming/scripting languages are inferior because of your inflexible, indoctrinated knowledge about programming, just learn something more, read specs, those are small for what the language offer in JS case, and stop moaning about null and undefined, falsy, and all those script thing stat made scripting historically easier and often more productive than strict programming languages.
The "you" I have used here is not about you .. you know these things, so why even bothering calling them pitfalls ... pitfalls are those nobody can understand, your 10 points are my breakfast, if you don't mind passing the metaphor ...
As summary, in my opinion, there's no need to write top 10 here: these are pointless, completely subjective, and always available, no matter which one is the topic.
These, are not what we need ... Object.observe idea/mechanism/possibility is, the fat null is == undefined is not stopping anyone, and never did, from creating amazing stuff with the Web or, lately, the server.
Just my 2 cents, don't take it personal, please!
br
[cc-ing es-discuss again]
Yes. I like the idea of JS(L|H)int as a teacher for newcomers!
On Sun, Jan 6, 2013 at 5:06 AM, Axel Rauschmayer <axel at rauschma.de> wrote:
<snip>
This is not about pointing out how bad JavaScript is, it is about collecting things that confuse people who are new to the language. They help those people to learn what you already know. Many people really hate JavaScript. Some of those, we’ll never convert, they’d rather program Java than JavaScript (as you point out above). But some do cite valid WTFs. Some of those WTFs even get you if you know the language well (e.g.
this
in non-method functions). Thankfully, ES6 will fix many of those. It’ll prove the haters wrong who say that JavaScript is beyond fixing.
Well, with this explanation, I understand better the sense of your three first "biggest pitfalls": They are just things that confuse people coming from some other definite programming language and accustomed to different semantics.
You probably won't say that the OO model of Java (for example) is a pitfall (even less a "biggest" one), even if it may confuse people new to OO-programming; rather it is a handful feature that works well. In the same vein, the notion of truthy/falsy, the sloppy == operator, and the distinction between null and undefined, do not deserve to be called "pitfalls" or "warts", for they are indeed positively useful features (and much easier ones than classes and objects) that work without problem once you have correctly learned them.
(BTW, "Some of those, we’ll never convert, they’d rather program Java than JavaScript." I bet that those people don't know how much they lose by not having proper first-class functions ;-)
On Sunday, January 6, 2013, Axel Rauschmayer wrote:
Exactly Brendan, I could not agree more and this is my No. 1 pitfall about JS: developers often not doing real work complaining about stuff that developers doing real work don't even care about or never ever had to worry about.
I don’t follow. Who are these people not doing “real work”? And I don’t think discussing the language qualifies as complaining.
In any case they can learn and understand the feature/problem using the feature when needed, avoiding its weakness when necessary.
About falsy and truthy, null and undefined, who cares ... seriously, and to be honest, that's not a pitfall, is rather a feature when needed as it is for all other scripting languages as long as you know what you are doing ... and no programming language will save you if you don't know what you are doing and it's your duty, as developer, to understand the language you are using if that's your job.
“Warts” is probably a better term than “pitfalls”.
Again, about falsy ... if I see a glass empty, it does not mean I used a microscope to understand no water is left in the whole glass surface ... I just consider that empty and I add water on top.
Engineers have the tendency to over complicate even simple tasks as the one I've just described ... what is the benefit? What is the result? That the day falsy values in JS will disappear libraries authors will implement an isFalsy(value) function/method and use it 90% of the time regretting the trick with == disappeared ... isn't it ;-)
What is the trick with ==? Note that == does not respect truthiness or falsiness:
> 2 == true false > 2 == false false > '2' == true false > '2' == false false
None of these (above) abstract comparison operations represents "truthy" or "falsy" behaviour.
0 == false; // true 11.9.3.7 converts to 0==0
"" == false; // true 11.9.3.7 converts to ""==0 11.9.3.5 converts to 0==0
1 == true; // true 11.9.3.7 converts to 1==1
"" becomes +0 per 9.3.1 +0 becomes false per 9.2 true becomes 1 per 9.3 table 12
The unary logical NOT ! also converts with ToBoolean.
2==true is just the number two compared with the Boolean true, which aren't equal in value or type and have no criteria for conversion.
Note that == does not respect truthiness or falsiness:
> 2 == true false > 2 == false false > '2' == true false > '2' == false false
None of these (above) abstract comparison operations represents "truthy" or "falsy" behaviour.
0 == false; // true 11.9.3.7 converts to 0==0
"" == false; // true 11.9.3.7 converts to ""==0 11.9.3.5 converts to 0==0
1 == true; // true 11.9.3.7 converts to 1==1
"" becomes +0 per 9.3.1 +0 becomes false per 9.2 true becomes 1 per 9.3 table 12
The unary logical NOT ! also converts with ToBoolean.
2==true is just the number two compared with the Boolean true, which aren't equal in value or type and have no criteria for conversion.
I am aware (2==true converts to 2==1). But I was surprised when I first found out. The above was slightly off-topic, because I misunderstood what trick Andrea was referring to.
Axel, here ... that chapter is really a tiny one and every dev should read that at least once if JS is, by accident, their programming language: webreflection.blogspot.com/2010/10/javascript-coercion-demystified.html
br
[Starting a new thread, just in case.]
if
]arguments
becomes obsolete in ES6]let
FTW in ES6]super
in ES6]this
in non-method functions: 9a) Referring to thethis
of a surrounding method, 9b) accidentally creating globals by forgettingnew
, 9c) using methods as callback functions [(a) and (b) fixed by ES6 arrow functions and ES5 strict mode]Thus: 1-3 won’t go away soon. 4-10 are mostly eliminated by ES6.
Deliberate omissions:
There are more pitfalls, but these seemed the biggest ones.