Is there any place where it is specified exactly how the properties of localStorage work? The only thing I found in the standards is:
value = storage.getItem (key)
value = storage[key]
Returns the current value associated with the given key, or null if the given key does not exist.
storage.setItem (key, value)
storage[key] = value
Sets the value of the pair identified by key to value, creating a new key/value pair if none > existed for key previously.
But it doesn’t say anything about what happens if key is a special property name like length or setItem (or __proto__ or constructor).
In practice this seems to work as one would expect: “special” properties work as usual, and only “unused” property names can be accessed with the bracket syntax.
But stuff gets a bit weird if you look at the details. At least on Chrome, if you add a value using as key a “special” property name, like localStorage.setItem('length', 'test'), then, somehow, Object.keys(localStorage) does include 'length' in the result. Also, if you try Object.getOwnPropertyNames(localStorage), 'length' is included. But if you try Object.getOwnPropertyDescriptors(localStorage), no property named 'length' is returned.
I can’t find any specification, or even description, of how these work. The ECMA standard mentions that “exotic” objects can override “abstract operations” with special behavior. But I couldn’t find any specs for how that is supposed to work, exactly, with localStorage. All that I could see are cursory mentions about behavior in specific browsers, and those are usually old and I presume obsolete.
So, in short: Is the detailed behavior of the localStorage specified anywhere?
Related
I have an odd situation in my Javascript code which I cannot explain. So far it has been observed only on Safari/Mac.
I have a line like this:
dict[id].sliceHovered = true;
And sometimes it throws an error:
Attempted to assign to readonly property.
Also:
dict is a bare blank object which I create myself with dict={}.
id is supplied by outside data, so it can be anything (I don't yet know which particular value causes this).
sliceHovered is obviously not a name of something that Javascript has built
in.
The objects in the dict are of my own type. They have a sliceHovered member, but it's not a Javascript defined property (as in Object.defineProperty()), just a regular property (the constructor executes this.sliceHovered=false).
"use strict" is on.
Object.freeze(), Object.seal(), Object.preventExtensions() and const are not used anywhere in the entire codebase.
Thus it's extremely puzzling as to how such an error could be thrown here. If I had an indexing error and dict[id] would be undefined or null, the error would be different. My only idea is that since the dict is created as dict={} then it inherits from Object and maybe id maps to some inherited property. But that means that the object returned from dict[id] would have to be read-only itself, because sliceHovered is definitely not a name of an existing Javascript property.
However I cannot think of any Javascript objects that would be intrinsically read-only like that.
Any ideas what could be wrong here?
You can check this situation
My only idea is that since the dict is created as dict={} then it inherits from Object
with: var dict = Object.create(null);
Also try to use Object.getOwnPropertyDescriptor(dict, id) to make sure descriptors have right values.
Last time I checked, the following two lines returned true:
null == localStorage["foo"];
null == localStorage.getItem("foo");
Same applies when replacing null with undefined.
So the first question is, why are there two ways to address the localStorage? And why does
localStorage["foo"]
return undefined while
localStorage.getItem("foo")
returns null?
Do I need to take care of that when developing JS?
The Web Storage Specification requires that .getItem() returns null for an unknown key.
Note however that .getItem() and .setItem() are specifically defined in the IDL as being the designated getter and setter for the Storage interface, and therefore they're also fully supported ways of accessing the contents of the storage.
However the [] syntax is more akin to a normal object and/or array property getter, and like those returns undefined for an unknown property name.
The reason not to use [] syntax is that it will operate on object properties first and will quite happily allow you to overwrite real properties and methods of the localStorage object, c.f:
> localStorage['getItem'] = function() { return 0 }
> localStorage.getItem('getItem')
0
localStorage["..."] is invalid usage of localstorage. You are trying to access methods of the localstorage object, rather than accessing actual database.
You have to use
localStorage.getItem("...")
and
localStorage.setItem("...")
methods to access storage database.
In javascript you always get an undefined value for keys that does not exist inside an object.
a = {}; //new object
alert(a["test"]); // you get 'undefined' because "test" keys is not found
In localStorage .getItem is a method who does check keys inside the localStorage object and returns null if not found.
Don't blame javascript, it's just the localStorage object behaviour
I recently asked a question about LocalStorage. Using JSON.parse(localStorage.item) and JSON.parse(localStorage['item']) weren't working to return NULL when the item hadn't been set yet.
However, JSON.parse(localStorage.getItem('item') did work. And it turns out, JSON.parse(localStorage.testObject || null) also works.
One of the comments basically said that localStorage.getItem() and localStorage.setItem() should always be preferred:
The getter and setter provide a consistent, standardised and
crossbrowser compatible way to work with the LS api and should always
be preferred over the other ways. -Christoph
I've come to like using the shorthand dot and bracket notations for localStorage, but I'm curious to know others' take on this. Is localStorage.getItem('item') better than localStorage.item or localStorage['item'] OR as long as they work are the shorthand notations okay?
Both direct property access (localStorage.foo or localStorage['foo']) and using the functional interface (localStorage.getItem('foo')) work fine. Both are standard and cross-browser compatible.* According to the spec:
The supported property names on a Storage object are the keys of each key/value pair currently present in the list associated with the object, in the order that the keys were last added to the storage area.
They just behave differently when no key/value pair is found with the requested name. For example, if key 'foo' does not exist, var a = localStorage.foo; will result in a being undefined, while var a = localStorage.getItem('foo'); will result in a having the value null. As you have discovered, undefined and null are not interchangeable in JavaScript. :)
EDIT: As Christoph points out in his answer, the functional interface is the only way to reliably store and retrieve values under keys equal to the predefined properties of localStorage. (There are six of these: length, key, setItem, getItem, removeItem, and clear.) So, for instance, the following will always work:
localStorage.setItem('length', 2);
console.log(localStorage.getItem('length'));
Note in particular that the first statement will not affect the property localStorage.length (except perhaps incrementing it if there was no key 'length' already in localStorage). In this respect, the spec seems to be internally inconsistent.
However, the following will probably not do what you want:
localStorage.length = 2;
console.log(localStorage.length);
Interestingly, the first is a no-op in Chrome, but is synonymous with the functional call in Firefox. The second will always log the number of keys present in localStorage.
* This is true for browsers that support web storage in the first place. (This includes pretty much all modern desktop and mobile browsers.) For environments that simulate local storage using cookies or other techniques, the behavior depends on the shim that is used. Several polyfills for localStorage can be found here.
The question is already quite old, but since I have been quoted in the question, I think I should say two words about my statement.
The Storage Object is rather special, it's an object, which provides access to a list of key/value pairs. Thus it's not an ordinary object or array.
For example it has the length attribute, which unlike the array length attribute is readonly and returns the number of keys in the storage.
With an array you can do:
var a = [1,2,3,4];
a.length // => 4
a.length = 2;
a // => [1,2]
Here we have the first reason to use the getters/setters. What if you want to set an item called length?
localStorage.length = "foo";
localStorage.length // => 0
localStorage.setItem("length","foo");
// the "length" key is now only accessable via the getter method:
localStorage.length // => 1
localStorage.getItem("length") // => "foo"
With other members of the Storage object it's even more critical, since they are writable and you can accidently overwrite methods like getItem. Using the API methods prevents any of these possible problems and provides a consistent Interface.
Also interesting point is the following paragraph in the spec (emphasized by me):
The setItem() and removeItem() methods must be atomic with respect to failure. In the case of failure, the method does nothing. That is, changes to the data storage area must either be successful, or the data storage area must not be changed at all.
Theoretically there should be no difference between the getters/setters and the [] access, but you never know...
I know it's an old post but since nobody actually mentioned performance I set up some JsPerf tests to benchmark it and as well as being a coherent interface getItem and setItem are also consistently faster than using dot notation or brackets as well as being much easier to read.
Here are my tests on JsPerf
As it was mentioned, there is almost no difference except of nonexisting key. The difference in performance varies depending on what browser/OS are you using. But it is not really that different.
I suggest you to use standard interface, just because it is a recommended way of using it.
The behaviour of the delete operator seems very complicated and there are many misunderstandings about what it actually does. To me, it seems that reassigning something to undefined will more reliably do what you would expect.
I've never seen the delete keyword in Javascript actually used in non-example code and I am wondering if it is particularly useful for anything. Does delete have any purpose that cannot be acheived by reassignment to undefined? Is it used at all in any of the famous libraries (e.g. jQuery, dojo, backbone, etc)?
Does delete have any purpose that cannot be acheived by reassignment to undefined?
Yes. If you want to unmask a property from a prototype or cause in, hasOwnProperty, and for (...in...) to not record the property as existing then delete is appropriate.
let set = {};
set._x = true;
alert('_x' in set); // true
set._x = undefined;
alert('_x' in set); // true
delete set._x;
alert('_x' in set); // false
EDIT: As T.J. Crowder explains:
The purpose of the delete operator is to completely remove a property from an object, whereas setting a property to undefined just sets the property to undefined.
This matters in its own right, but it also matters when you're using inheritance, because if O derives from P
let P = { prop: 42 };
let O = Object.create(P); // P is O's prototype.
when you retrieve O.prop, you get the value of prop from O if O has a property with that name (even if its value is undefined), but if O doesn't have the property at all, then the value will be retrieved from P.prop instead.
console.log(O.prop); // "42" since O doesn't have its own prop, but P does.
O.prop = undefined;
console.log(O.prop); // "undefined" since O has its own prop.
delete O.prop;
console.log(O.prop); // "42" since the delete "unmasked" P.prop.
As Mike Samuel points out in his answer, one of the most common usages of delete is when you are treating an object as a "property bag" that associates names with values. There is logically a difference between "this name is now mapped to some bogus value" and "this name is not mapped at all". "delete" achieves the latter.
That's all reasonably well understood. I thought I might add an interesting historical note regarding the JScript 1.0 through 5.0 engines.
In those original Microsoft implementations of JScript we used OLE Automation-style IDispatch objects to implement expando objects. IDispatch of course works by associating a name with a "dispatch id", which is simply an integer. To invoke dynamically, first you ask the dispatch object to give you the dispatch ID associated with a name, and then you say "now invoke the method associated with this ID, given these arguments".
That's all well and good. But one of the requirements of the IDispatch contract is that the mapping from name to dispatch ID be stable over the entire lifetime of the object. So if someone says "add property Foo to this object", then we might decide that property Foo is associated with dispatch identifier 0x1234 in that object. From that moment on, every time the object is asked for the dispatch identifier of "Foo", it must give back 0x1234, even if Foo is deleted and subsequently added again. This permits a caller to maintain their own fast cache of name/dispid pairs rather than always having to ask the object on every invocation.
The practical upshot of that is that "delete" does not in any way lessen the memory burden on the object in that implementation! When you delete a property (in the original implementation) we must add a bit to the object marking that dispatch identifier as deleted, but we must retain all the information about the name/id pairing in case that name ever comes back. Adding a huge number of properties to an object and then deleting all of them does not shrink the object in memory.
The JScript engine has of course been completely rewritten since my time (except for, I believe, the parser and lexer) so I have no idea if the engine still has this unusual quirk. It would be interesting to find out.
If you do
delete Foo.Bar;
it deletes the property Bar from object Foo entirely
Foo.Bar = undefined
merely sets Bar property to undefined and Foo.Bar still exists
The other answers are explaining the motivation behind the delete keyword. I would like to add that as of 2017, browser do deallocate memory both when deleting a property and when setting the property to undefined.
Consider this example (source of roughSizeOfObject()):
> var obj = {a:42,b:"b"}; roughSizeOfObject(obj)
26
> obj.a = undefined; roughSizeOfObject(obj)
18
> delete obj.a; roughSizeOfObject(obj)
10
> obj.b = undefined; roughSizeOfObject(obj)
8
> delete obj.b; roughSizeOfObject(obj)
0
The example comes from Chrome 61 (64-bit) console (note that all characters in String are internally encoded as 16-bit unsigned integer).
You can check the answer of the following link Can I set variables to undefined or pass undefined as an argument? which explains the difference in a very detailed way.
Summary:
You can certainly assign undefined to it, but that won't delete the
variable. Only the delete object.property operator really removes
things.
delete is really meant for properties rather than variables as such.
Browsers will let you get away with straight delete variable, but it's
not a good idea and won't work in ECMAScript Fifth Edition's strict
mode. If you want to free up a reference to something so it can be
garbage-collected, it would be more usual to say variable= null.
Well, you'd end up with an element in your object that contains the value undefined. The key wouldn't be gone.
What are expando objects in javascripts?
For what purpose we need this ? Any complete example will be appreciated
I found 1 article here Javascript: The red-headed stepchild of web development
Well, in javascript, any object is an expando object. What it means is, as the article covers, that whenever you try to access a property1 it will automatically be created.
var myObj = {}; // completely empty object
myObj.myProp = 'value';
The moment you assign myProp a value, the property myProp is dynamically created, eventhough it didn't exist before. In a lot of other languages, such as C#, this is not normally possible (actually C# has just enabled expando object support as well, but that's besides the point). To access a property in a normal class in C#, you need to specify in the class that it does indeed have this property.
1 Not quite correct. See npup's comment below for clarification.
Everything except primitive types(string, number,boolean) are objects and support Key:values structure. properties(keys) can be accessed and set using the dot notation as well as the square brackets.
var myObj = {};
myObj.myProp1 = 'value1'; //works, an expando property
myObj[myProp2] = 'value2'; // doesn't work, myProp2 is an undefined name.
myObj['myProp2'] = 'value2'; // works , an expando property
myObj[2010]= 'value'; //note the key is number, still works, an expando property??
myObj.2010 = 'value'; // FAILS. to use dot notation, key must be a string
An article written in 2007 that uses document.all (as the only way to access elements)? That's a big red flag.
It is just dressing up "You can add properties to an object" with some buzzwords.
We need to be able to do this because otherwise we wouldn't be able to store data, and that would make JavaScript a pretty useless language.
(Everything is an array? No it isn't. And it iterates over an object without a hasOwnProperty wrapper. That isn't safe. Just keep away from the article, it is worse than useless)
JavaScript turns elements with specific IDs of names into expandos of the returned DOM object. It is explained here.