Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
iOS 8 length bug breaks jQuery and Underscore object iteration (firstdoit.com)
63 points by gadr90 on April 1, 2015 | hide | past | favorite | 33 comments


The thing that shocks me most in this article is the subtleness of the bug inducing this behaviour: http://trac.webkit.org/changeset/182058


Yep! Somebody explained quite clearly the bug over at reddit: http://www.reddit.com/r/webdev/comments/3104m3/ios_8_insidio...

I quote:

> There is not length property inserted on the object, the jQuery folks had already confirmed that. It is a branch on the form of object that was failing due to a bug in the linkage of a jump. That made that bug very bizarre: you can access a length value by identifier, but now get a length property on the same object.


I didn't realize anyone used integers as keys. That feels so unnatural to me. I'm also surprised any libraries would check for .length to see if it's an array or not; there are plenty of objects you may want to create with a length property. It seems to me this would be a more accurate check:

Object.prototype.toString.call(maybeArray) === "[object Array]"

Also sometimes I feel like I'm the only one who doesn't like to use underscore or jQuery for things like this; iterating over properties is so easy it seems weird to me to use a library to create a new object for me to then iterate through.


It's easy if your browser has Object.keys or nobody has mucked around with Object.prototype. As for the ease of testing if something is an array, figuring out if something is an array is relatively easy but array like objects like arguments and domlist are where length checking comes from.


> It's easy if your browser has Object.keys

Most browsers can do a for..in. At least for me there is no browser in existence that I care about that doesn't have that :)

> nobody has mucked around with Object.prototype

That's just what object.hasOwnProperty(key) is for. An annoying extra check but not a big deal.

> As for the ease of testing if something is an array, figuring out if something is an array is relatively easy but array like objects like arguments and domlist are where length checking comes from.

There is an incredibly limited list; I'd just check for those few items instead of relying on a property existing that other objects could use but I digress.


> Most browsers can do a for..in. At least for me there is no browser in existence that I care about that doesn't have that :)

then why are you using the underscore/lodash/jquery method, it's main advantage (besides working on array like objects) is compatibility for older browsers..

I'm, btw, on your side here and just use Object.keys and move on for objects and use a while loop for array like objects.


> then why are you using the underscore/lodash/jquery method, it's main advantage (besides working on array like objects) is compatibility for older browsers..

Oh I'm not my point was it's not really necessary anymore. It's been so long since browsers didn't have support for these kinds of things I can't imagine the compatibility is that important anymore except in say corporate environments.


I came across an equally strange bug a year or so ago.

http://stackoverflow.com/questions/22139746/ios-javascript-e...

I found a way to reproduce it, put in a bug report with apple and never got a response.


The workaround in the article was:

    var thingsGroupedById = _.groupBy(things, function(t){
      return 'seller' + t.sellerId
    });
    // { seller1: [...], seller2: [...] }
But I actually would have failed this in code review:

    _.map(thingsGroupedById, function (things) {
      // ...
    });
and replaced it with:

    _.map(_.keys(thingsGroupedById), function (thingKey) {
      var thing = thingsGroupedById[thingKey];
      // ...
    });
because although mapping over objects is "normal", it has never been 100%.


Why not instead do

    _.map(_.values(thingsGroupedById), function(thing) {
        // operate on thing
    }
Then there's no need to do the property access yourself.

Chaining makes this even nicer:

    _(thingsGroupedById).values().map(function(thing) {

    }


Depends on what //... is. You might need the key for something, and you can get the value from the key but not vice versa.


This interests me very much. Are there known problems about mapping over objects? What do you mean it has never been 100%? Thanks!


The latest version of underscore has a _.mapObject function.


The crazy bug aside, I could have done without the animated image macros.


Everyone vents in their own way. I've used stupid pictures before to get over spending a whole afternoon fixing someone else's screw up.


I ran into this bug myself, and I spent an afternoon or so debugging it.

A simple library upgrade (Underscore 1.7.0 -> 1.8.2) broke a key feature of a site I was working on, but only on my iPhone. It worked just fine on my iPad running the same exact version of iOS.

I ended up using git bisect to narrow down the commit, which consisted of several minor updates to various packages. Of those packages, it was my 3rd or 4th guess as to which would have caused that failure (it wasn't clear since I was having trouble connecting the Safari debugger).


Array.isArray() would be more reliable than testing if it has a property "length" :/

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


It's not quite so simple since JavaScript has the concept of array-like objects - basically objects with a length property and numeric keys (but which aren't arrays).

From the Underscore documentation: "Collection functions work on arrays, objects, and array-like objects such as arguments, NodeList and similar".

So basically this bug can lead to false positives when checking for array-like objects.


Except as MDN says, that works in IE9+, and jQuery 1.x works in IE6+.


MDN has a polyfill for older browsers, right below the examples.


For an equally crazy bug that's affecting Angular and several other major libraries:

https://github.com/angular/angular.js/issues/9128#issuecomme...

Basically strict mode blows up webkit in odd ways.


I noticed an instance of _.size(obj) returning "undefined" on a collection using number indices, but in each instance_.keys(obj).length would work fine.

This was occurring on iOS 7.1.2 on a single device, from time to time. I realize after reading this, that this might be related to the WebKit bug, since it was the only 64 bit ARM device we had at the time.

The bug disappeared when I simply used _.keys(obj).length instead of _.size(obj). It was very hard to reproduce and I could never isolate it in a sample...

It's NOT the same bug though. It's kind of the opposite. The "length" property would disappear. Would that be caused by the same WebKit bug? Or that's a new one?


Not to be a jerk, but why not use objects for key value and arrays for int value?

Yes, JavaScript object/array system has it's warts, but to me that's clearly the way it was intended to be used.


The article described the type of situation this arises in: when you want to use arbitrary data as keys of a map, for example when grouping a collection of objects by one of their properties. Any implementation of a hash/map/dictionary should support this process, whether or not it coerces the types of its keys.

The problem with the engine in question is that it makes a hash change its behavior when all keys are integers.


Be great if there was an AdBlock but for animated gifs in blog articles.


Maybe there's a ClickToPlay for animated GIFs/PNGs?

Edit: There is: https://addons.mozilla.org/en-US/firefox/addon/toggle-animat...


I will add something like that myself soon enough. Thanks for pointing it out! I realize, now, that it bothers some people.

https://github.com/firstdoit/firstdoit.com/issues/14


I think the OP likes Emma Stone


People still use Underscore?


I am aware of underscore as a nice blob of simple utility functions. I'm not aware of what you would replace it with?


lodash is basically an underscore replacement. https://lodash.com/


For a second there I thought we might get a thread that mentions Underscore without Lodash being plugged.

Oh well, maybe next time.


While the map() should be written in pure JS standard lib, the _.groupBy() is reasonable.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: