This works fine:
$cookies.animals = []
$cookies.animals.push({cat: 'meow'})
console.log($cookies.animals) -> [Object]
But identical code inside a factory function doesn't work:
addAnimal: function(id, name, price){
console.log($cookies.animals) //-> [object Object]
$cookies.animals.push({cat: 'meow'}) //-> TypeError: undefined is not a function
}
So why am I getting a TypeError?
If I do this
addAnimal: function(id, name, price){
$cookies.animals = []
console.log($cookies.animals) //-> []
$cookies.animals.push({cat: 'meow'}) //-> works
}
It works (though of course it resets the array) so some something weird is happening to $cookies.animals.
If we look at my console.log inside the factory function:
We get this:
[object Object]
Object with a capital O is my cat, but that weird object is doing something evil I think. Where does it even come from?
Really don't know what's going on to be quite honest. Help please. All I'm trying to do is append to an array...
Per the AngularJS $cookie documentation:
Only a simple Object is exposed and by adding or removing properties to/from this object, new cookies are created/deleted at the end of current $eval. The object's properties can only be strings.
Your initial code works because you can, at any time, set any type of property on any object. Angular does not hold onto your non-string values, though, so your animals property is no longer set in the latter use context. You'll have to serialize and deserialize (probably via JSON) when writing and reading (respectively).
So you'll need to do something like this to initialize the cookie:
var animals = [];
animals.push('Furry Cat');
$cookies.animals = JSON.stringify(animals);
Later when reading, you'd need to do this:
var animal_cookie_value = $cookies.animals,
animals = animal_cookie_value ? JSON.parse(animal_cookie_value) : [];
As for your added remark of:
Object with a capital O is my cat, but that weird object is doing something evil I think. Where does it even come from?
When you see
object Object
in JavaScript you are seeing the output of the default toString() method on JavaScript objects. In other words, you used an object in context of a string, so JS cast it to string (which results in the value you're questioning).
FWIW, I develop a JavaScript cookie library which allows you to pass in any data and it handles the serialization for you. It is currently hosted on Google code, but I am working on moving it to GitHub and hope to add an AngularJS module for it someday.
Related
This is a question about Javascript(and possibly other languages) fundamentals.
I was developing a project and at some point a realized I had defined the same key more than once in an object but no error appeared on console. After some research I couldn't find a clear/official statement to what happens in this situation or how this affects the program. All I could find out is the output in a simple example:
let obj = {key1:1, key1:2, key1:3};
console.log(obj.key1);// >> output = 3
console.log(Object.keys(obj));// >> output = ['key1']
My question is: The value is just redefined and all previous declarations are erased? Is this a limitation or some kind of error of Javascript?
The value is just redefined and all previous declarations are erased?
Yes. When an object literal contains duplicate keys, only the final key in the object literal will exist on the object after the object is evaluated.
Is this a limitation or some kind of error of Javascript?
It's permitted, but it's nonsense. Like lots of things in programming, there are things which are syntactically allowed but don't make any sense in code.
But you can use a linter to prevent from making these sorts of mistakes, eg ESLint's no-dupe-keys.
There's one case where something very similar to duplicate keys can be common, which is when using object spread, eg:
const obj = { foo: 'foo', bar: 'bar' };
// Now say we want to create a new object containing the properties of `obj`
// plus an updated value for `foo`:
const newObj = { ...obj, foo: 'newFoo' }
This sort of approach is very common when working with data structures immutably, like in React. It's not exactly the same thing as an outright duplicate key in the source code, but it's interpreted the same way - whatever value gets interpreted last in the key-value list (regardless of if the key is spread or static) will be the value that the final object contains at that key.
I understand that when index names are used to push values in Javascript, they essentially work like objects. But what I don't understand is the following behaviour -
person = [];
person[0] = "Someone";
person["test"] = "SomeoneElse"
Inputting person on the console prints ["Someone"] and I could see no information about person.test.
person.test does print SomeoneElse. However, if I go console.log(person), I get ["Someone", test: "SomeoneElse"].
Curious to check if this makes sense, I tried to create a structure like this one -
var experiment = ["Someone1", test1: "SomeoneElse1"]
and what I get is
Uncaught SyntaxError: Unexpected token
What am I missing?
Thanks in advance!
Typing person on the console prints ["Someone"].
Array.prototype.toString formats this output, and it only considers the "array values" of itself without other properties.
However, if I go console.log(person), I get ["Someone", test: "SomeoneElse"].
console.log outputs other information about the object, including own properties.
Uncaught SyntaxError: Unexpected token
Because that is bogus syntax; the array literal syntax doesn't allow keys, because array values aren't supposed to have keys. Arrays are a numerically indexed list of values. Merely by the fact that under the hood those lists are implemented using objects (because everything in Javascript is an object of some kind or another) are you able to set "non numeric keys" on the array. That doesn't mean you're using the array correctly though.
Also see Are JavaScript Array elements nothing more than Array object properties?
This is because an array in JavaScript is also an object itself, and objects can have properties. So it is perfectly valid to have an array with elements, but also have properties set on the array object.
The second example doesn't work because the [...,...,...] syntax is specifically for instantiating an array and its elements.
typing person in console is like having an alert(person); or passing its value to a variable or element, so it is more like you want to get the first set of readable values. That is the reason why it is showing you the values inside, you can try adding person[1] = *something;*, then it will display someone, something
console.log(person) - it displays all the items inside an object. It is more like informing you of what is inside, like a text visualizer in your IDE
var experiment = ["Someone1", test1: "SomeoneElse1"] - it will absolutely throw an exception there is no such format like this on initializing values, at least it is expecting an array format like var experiment = ["Someone1", "SomeoneElse1"]
I'm reading on the map/reduce documentation and this particular string example doesn't make sense to me.
var toCode = function(char) {
return char.charCodeAt(0);
}
First, the thing that works. Why does it work? string is not an array.
var text = "Hello World";
var map = Array.prototype.map;
console.log(map.call(text, toCode));
Now, the thing that doesn't work. Isn't this exactly the same as above?
console.log(text.map(toCode));
I use www.codeacademy.com console to test and this is the error message:
TypeError: undefined is not a function (evaluating '"hello world".map(toCode)')
Thank you.
Your second code won't work because strings don't have a map method, but you can borrow it from Array, because array methods are meant to work on array-like objects, that is an object with numeric keys, such as strings:
The map function is intentionally generic; it does not require that
its this value be an Array object. Therefore it can be transferred to
other kinds of objects for use as a method. Whether the map function
can be applied successfully to a host object is
implementation-dependent.
http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.19
I have a JS object
{
aString:[aNumber, aURL]
}
JSON.stringify() returns
{
"aString":"[number, \"aURL\"]"
}
I thought that valid JSON can have arrays as values. Can I have stringify return the JSON string without converting the array into a string? Basically I need turn the whole JS object straight into a string, without any modification.
Is there a better way to do this? I've been looking around but everyone suggests using JSON.stringify if I want an object to string, and no one has raised this problem.
EDIT: Thanks for the quick responses. Here is how I created my JS object, please let me know if I messed up and how!
cookie = {};
// productURL is a string, timer is a number, imageSrc is a URL string
cookie[productURL] = [timer, imageSrc];
// then, I just stringified cookie
newCookie = JSON.stringify(cookie);
If it is also relevant, I am setting an actual cookie's value as the resulting JSON string, in order to access it in another set of functions. Setting the cookie's value does do some URI encoding of its own, but I've actually been grabbing the value of newCookie in the Chrome console as well and it also returns the Array as a string.
If an object you're trying to stringify has a toJSON function, that will be called by JSON.stringify. Most likely you have an external library that's adding Array.prototype.toJSON.
For example, an old version (1.6) of Prototype JS will "conveniently" add that for you.
Prototype 1.6.1:
alert(JSON.stringify([1, 2, 3]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.6.1/prototype.min.js"></script>
Whereas a newer version will not.
Prototype 1.7.2:
alert(JSON.stringify([1, 2, 3]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.min.js"></script>
You could try deleting Array.prototype.toJSON just to see if that's what's causing the problem. If it is, you might want to look into upgrading/deprecating any libraries in your code that do weird things like that.
Prototype 1.6.1 (after deleting toJSON)
delete Array.prototype.toJSON;
alert(JSON.stringify([1, 2, 3]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.6.1/prototype.min.js"></script>
Based on your description this is not what should happen.
If you have code like this:
var obj = {
aString:[123, "test"]
}
document.getElementById("out").value = JSON.stringify(obj);
it will generate the expected json:
{"aString":[123,"test"]}
also see https://jsfiddle.net/zudrrc13/
in order to produce your output the original object would have to look something like:
var obj = {
aString:"[123, \"test\"]"
}
I'm experiencing an odd behavior (maybe it isn't odd at all but just me not understanding why) with an javascript array containing some objects.
Since I'm no javascript pro, there might very well be clear explanation as to why this is happening, I just don't know it.
I have javascript that is running in a document. It makes an array of objects similar to this:
var myArray = [{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...];
If I print out this array at the place it was created like JSON.stringify(myArray), I get what I was expecting:
[{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...]
However, if I try to access this array from a child document to this document (a document in a window opened by the first document) the array isn't an array any more.
So doing JSON.stringify(parent.opener.myArray) in the child document will result in the following:
{"0":{"Id":"guid1","Name":"name1"},"1":{"Id":"guid2","Name":"name2"},...}
And this was not what I was expecting - I was expecting to get the same as I did in teh parent document.
Can anyone explain to me why this is happening and how to fix it so that the array is still an array when addressed from a child window/document?
PS. the objects aren't added to the array as stated above, they are added like this:
function objTemp()
{
this.Id = '';
this.Name = '';
};
var myArray = [];
var obj = new ObjTemp();
obj.Id = 'guid1';
obj.Name = 'name1';
myArray[myArray.length] = obj;
If that makes any difference.
Any help would be much appreciated, both for fixing my problem but also for better understanding what is going on :)
The very last line might be causing the problem, have you tried replacing myArray[myArray.length] = obj; with myArray.push(obj);? Could be that, since you're creating a new index explicitly, the Array is turned into an object... though I'm just guessing here. Could you add the code used by the child document that retrieves myArray ?
Edit
Ignore the above, since it won't make any difference. Though, without wanting to boast, I was thinking along the right lines. My idea was that, by only using proprietary array methods, the interpreter would see that as clues as to the type of myArray. The thing is: myArray is an array, as far as the parent document is concerned, but since you're passing the Array from one document to another, here's what happens:
An array is an object, complete with it's own prototype and methods. By passing it to another document, you're passing the entire Array object (value and prototype) as one object to the child document. In passing the variable between documents, you're effectively creating a copy of the variable (the only time JavaScript copies the values of a var). Since an array is an object, all of its properties (and prototype methods/properties) are copied to a 'nameless' instance of the Object object. Something along the lines of var copy = new Object(toCopy.constructor(toCopy.valueOf())); is happening... the easiest way around this, IMO, is to stringency the array withing the parent context, because there, the interpreter knows it's an array:
//parent document
function getTheArray(){ return JSON.stringify(myArray);}
//child document:
myArray = JSON.parse(parent.getTheArray());
In this example, the var is stringified in the context that still treats myArray as a true JavaScript array, so the resulting string will be what you'd expect. In passing the JSON encoded string from one document to another, it will remain unchanged and therefore the JSON.parse() will give you an exact copy of the myArray variable.
Note that this is just another wild stab in the dark, but I have given it a bit more thought, now. If I'm wrong about this, feel free to correct me... I'm always happy to learn. If this turns out to be true, let me know, too, as this will undoubtedly prove a pitfall for me sooner or later
Check out the end of this article http://www.karmagination.com/blog/2009/07/29/javascript-kung-fu-object-array-and-literals/ for an example of this behavior and explanation.
Basically it comes down to Array being a native type and each frame having its own set of natives and variables.
From the article:
// in parent window
var a = [];
var b = {};
//inside the iframe
console.log(parent.window.a); // returns array
console.log(parent.window.b); // returns object
alert(parent.window.a instanceof Array); // false
alert(parent.window.b instanceof Object); // false
alert(parent.window.a.constructor === Array); // false
alert(parent.window.b.constructor === Object); // false
Your call to JSON.stringify actually executes the following check (from the json.js source), which seems to be failing to specify it as an Array:
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
//stringify