On Sat, Jan 29, 2011 at 6:35 PM, Asen Bozhilov <[email protected]>wrote:
> The last week I explored some things about `localStorage'. The most > interesting and meanwhile confusing things are described in two > articles about `localStorage': > > <URL: > http://www.bennadel.com/blog/2105-Exploring-HTML5-s-localStorage-Persistent-Client-Side-Key-Value-Pairs.htm > > > <URL: http://hacks.mozilla.org/2009/06/localstorage/> > "The easiest way to use localStorage is to treat it like a regular object:" > > After reading these articles I start thinking about the setter and > getter of storage instance referred by `window.localStorage'. The > others questions are: > > Should I treat `localStorage' as a regular object? > Does it safe if I do? > > So I did some tests, to answer on my questions. > The test cases are in Firefox 3.6 under Debian. > > What happens with `localStorage' object when I put new item in > associated list trough `setItem' method? > > var hasOwnP = Object.prototype.hasOwnProperty, > storage = window.localStorage; > > storage.clear(); //Clear associated list > > storage.setItem('foo', 'bar'); > console.log(hasOwnP.call(storage, 'foo')); //true > console.log(storage.getItem('foo')); //bar > > storage.setItem('setItem', 'bar'); > console.log(hasOwnP.call(storage, 'setItem')); //false > console.log(storage.getItem('setItem')); //bar > console.log(typeof storage.setItem); //function > > storage.setItem('hasOwnProperty', 'bar'); > console.log(hasOwnP.call(storage, 'hasOwnProperty')); //false > console.log(storage.getItem('hasOwnProperty')); //bar > console.log(typeof storage.hasOwnProperty); //function > > Obviously `setItem' checks the prototype chain of `localStorage' for > passed key. If key exist it does not add new own property to > `localStorage' object. Just put this key/value in associated list. The > interesting part is if passed key does not exist in the prototype > chain of `localStorage', then it creates new own property of > `localStorage' and put key/value in associated list. > > Everything seems safe and clear, but what happens if I treat this > object as "regular" object? > var hasOwnP = Object.prototype.hasOwnProperty, > storage = window.localStorage; > > storage.clear(); //Clear associated list > > storage.setItem = 'bar'; > console.log(hasOwnP.call(storage, 'setItem')); //true > console.log(storage.getItem('setItem')); //bar > console.log(typeof storage.setItem); //string > > storage.hasOwnProperty = 'bar'; > console.log(hasOwnP.call(storage, 'hasOwnProperty')); //true > console.log(storage.getItem('hasOwnProperty')); //bar > console.log(typeof storage.hasOwnProperty); //string > > Now the setter does not check for existence of these properties names > in the prototype chain of `localStorage'. It directly add them as own > properties and also add them as key in associated list with this > storage instance. > > This answer on my two questions and the answer is, it's not really > safe to treat `localStorage' as regular object. When I have to add > key/value in the list I should use `setItem' method. > > Lets see the getter: > > var hasOwnP = Object.prototype.hasOwnProperty, > storage = window.localStorage; > > storage.clear(); //Clear associated list > > storage.setItem('foo', 'bar'); > console.log(storage.getItem('foo')); //bar as expected > console.log(typeof storage.foo); //string as expected > > storage.setItem('setItem', 'bar'); > console.log(storage.getItem('setItem')); //bar as expected > console.log(typeof storage.setItem); //function "unexpected" > > As I explained when `setItem' has called with `key` which exist as > property name in the prototype chain of `localStorage' it will not > create new own property name of `localStorage' with this name. From > this point comes the "unexpected" result. > > While in my example it's not really unexpected result, it can be if > there are third party extensions of `Object.prototype', because > `localStorage' inherit from `Object.prototype'. > > var hasOwnP = Object.prototype.hasOwnProperty, > storage = window.localStorage; > > storage.clear(); > > Object.prototype.foo = function () {}; > > storage.setItem('foo', 'bar'); > console.log(storage.getItem('foo')); //bar as expected > console.log(typeof storage.foo); //function > > > Hope this thread makes some clarifications the members of JSMentors! > [...] IIRC, the meta-characteristics of `localStorage` methods vary among implementations. In WebKit, for example, `localStorage` methods are writable (similar to FF3.6, as your tests show), so accidental (or malicious) assignment to `getItem` will overwrite it. I filed a bug with WebKit on this more than a year ago — https://bugs.webkit.org/show_bug.cgi?id=30996 but there's still no resolution there, so I think the behavior is unchanged. I'm guessing this problem doesn't bite people as often as I thought it would, which would explain why the "bug" is still open. Similar issue exists in Mozilla bug tracker at https://bugzilla.mozilla.org/show_bug.cgi?id=605054 But is it a bug really? As far as I know, web storage spec ( http://dev.w3.org/html5/webstorage/) makes no clarification on whether interface members are writable; or enumerable/configurable for that matter. WebIDL spec, however, does mention something about interface members having attributes { [[Writable]]: true, [[Configurable]]: false, [[Enumerable]]: false } (http://dev.w3.org/2006/webapi/WebIDL/#es-interfaces). So it seems like writability of localStorage's getItem, setItem, clear, etc. is actually in accordance to spec... which is probably not that great, since it only makes for a more fragile implementation. The opposite argument could be that [[writable]]: false would disallow "useful" cases of overwriting localStorage methods, whatever those cases might be. The issue with `setItem('...')` overwriting members of interface is another one, and would need to be addressed separately by a web storage spec, it seems. There's definitely nothing good about storage that's supposed to handle arbitrary key/values, breaking with a certain set of keys (i.e. those corresponding to interface members) :) -- kangax -- To view archived discussions from the original JSMentors Mailman list: http://www.mail-archive.com/[email protected]/ To search via a non-Google archive, visit here: http://www.mail-archive.com/[email protected]/ To unsubscribe from this group, send email to [email protected]
