10 biggest JS pitfalls

Brendan Eich brendan at mozilla.com
Sun Dec 30 14:57:15 PST 2012


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(result[3]()); // 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.
>
> http://jsperf.com/equ-vs-strict-equ
>
> 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. :-P

/be


More information about the es-discuss mailing list