137

Documentation: https://developer.mozilla.org/en-US/docs/Web/API/FileList

Why is FileList an object rather than an array? The only property it has is .length and the only method it has is .item(), which is redundant (fileList[0] === fileList.item(0)).

Val Kornea
  • 4,469
  • 3
  • 40
  • 41
  • 3
    Many APIs related to DOM and HTML are defined in a more data-structure-agnostic way. Think about `NodeList`s and `HTMLCollection`s. E.g. what if a language doesn't support the `foo[bar]` syntax? – Felix Kling Aug 15 '14 at 20:14

4 Answers4

173

Well, there could be several reasons. For one, if it were an array, you could modify it. You can't modify a FileList instance. Secondly but related, it could be (probably is) a view onto a browser data structure, so a minimal set of capabilities makes it easier for implementations to provide it.

You can convert it to an array via a = Array.from(theFileList) (that's an ES2015 method, but it's trivial to polyfill it) or via a = Array.prototype.slice.call(theFileList).

Update in 2018: Interestingly, though, the spec has a note on FileList:

The FileList interface should be considered "at risk" since the general trend on the Web Platform is to replace such interfaces with the Array platform object in ECMAScript [ECMA-262]. In particular, this means syntax of the sort filelist.item(0) is at risk; most other programmatic use of FileList is unlikely to be affected by the eventual migration to an Array type.

I find that note odd. I thought the trend was toward iterable, not Array — such as the update to NodeList marking it iterable for compatibility with spread syntax, for-of, and forEach.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 6
    Since you can't modify it, there's no reason for it to have `.push()` and other array methods. – Val Kornea Aug 15 '14 at 20:22
  • 2
    @VladimirKornea (time passes but...) well I have the use-case of validating file sizes, so "filter" from Array comes in handy. There are many others "read-only" methods. – estani Nov 15 '19 at 09:37
  • Does the note mean it would be risky to use `Array.from(theFileList)`? Thanks for your help! – Crashalot Dec 28 '20 at 08:52
  • @Crashalot - No, that should be just fine. I can't imagine `FileList` itself going away (though I could be wrong -- as the note says, most people's code treats it like an array), but apparently the `item` method may since it's not widely used. But the object will be there and be array-like, so `Array.from(theFileList)` gets you from the possibly-changing thing (the `FileList`) to a nice well-defined thing (an array). – T.J. Crowder Dec 28 '20 at 09:04
  • 1
    Thanks for the fast response and clarification. OK, we'll stick to using `Array.from(theFileList)` since it's not an actual array and we need it as an array for concatenation purposes. – Crashalot Dec 28 '20 at 09:39
27

I think it's its own Datatype because Object Oriented Programming was more of a thing than functional programming back when it was defined. Modern Javascript offers functionality to cast Array-like Datatypes to Arrays.

For example, like Tim described: const files = [...filesList]

Another way of iterating a FileList with ES6 is the Array.from() method.

const fileListAsArray = Array.from(fileList)

IMO it's more readable than the spread operator, but on the other hand it's longer code :)

Valentin Rapp
  • 432
  • 6
  • 11
  • It's true they tried to make JS look like Javas dumb little brother. I don't think though, OO at to blame here - the irony is that in many aspects Java itself wasn't very consistent in being object oriented. And I'd argue `FileList` not being an array is questionable especially from an OO standpoint. Other stuff as well, like having a dedicated `Math` object (that sorta made sense in Java) leaked through, even though with JS only number type this could have been done better. I guess it's all a consequence of being designed in a rush. – Oliver Schimmer Feb 21 '22 at 18:38
7

If you want use array methods on FileList try apply

So for example:

Array.prototype.every.call(YourFileList, file => { ... })

if you want to use every

machnicki
  • 491
  • 5
  • 3
2

Here's a couple of polyfills that add a toObject() function to File and a toArray() function to FileList:

File.prototype.toObject = function () {
  return Object({
    lastModified: parseInt(this.lastModified),
    lastModifiedDate: String(this.lastModifiedDate),
    name: String(this.name),
    size: parseInt(this.size),
    type: String(this.type)
  })
}

FileList.prototype.toArray = function () {
  return Array.from(this).map(function (file) {
    return file.toObject()
  })
}

var files = document.getElementById('file-upload').files
var fileObject = files[0].toObject()
var filesArray = files.toArray()
Kodie Grantham
  • 1,963
  • 2
  • 17
  • 27