What does JSON.stringify() does with functions in an object? - javascript

Assuming a Javascript object has a function:
function Foo() {
this.a = 32;
this.bar = function (){
alert("Hello World!");
}
}
How does JSON.stringify() deal with the bar function? Is it just ignored? Is this standard practice? What does JSON converts and doesn't convert into the Json string?
Rem: I don't know why people downvoted my question, it is obvious one should not stringify an object with a function. I was just trying to understand the default behavior.

JSON.stringify simply changes functions passed into null if passed into an array, and does not include the property in at all if in an object. It makes no attempt to replicate the function, not even in string form.
For example, see this:
JSON.stringify([function () {}]); // "[null]"
JSON.stringify({ x: function () {} }); // "{}"
If you attempt to stringify a function itself, it's not a valid JSON value, which means that JSON.stringify can't make a valid JSON string:
JSON.stringify(function () {}); // undefined
This is specified as part of the ECMAScript specification, though the actual wording of it is rather complex. However, a note is included that summarises this:
NOTE 5
Values that do not have a JSON representation (such as
undefined and functions) do not produce a String. Instead they produce the undefined value. In arrays these values are represented as
the String null. In objects an unrepresentable value causes the
property to be excluded from stringification.

Related

How to get type of this keyword in Javascript? [duplicate]

This question already has answers here:
Messing with this in prototype's method
(2 answers)
Closed 1 year ago.
I'm manipulating the prototype of Object so that I can add some extension methods.
I've found out that typeof operator always returns object in case the operand is this:
Object.prototype.logType = function () { console.log(typeof this); }
"Hello".logType()
output of code above is object instead of string. I know that in JavaScript everything is indeed an object, However I need to know the exact type of the value of this. How can I achieve that?
When you call a method on a primitive, JS automatically wraps that primitive in its associative object wrapper, so:
"Hello".logType()
becomes:
new String("Hello").logType()
hence, this inside of your logType function refers to the wrapped primitive value, giving you an object. You can call .valueOf() to grab the primitive value wrapped in the object:
Object.prototype.logType = function () { console.log(typeof this.valueOf()); }
"Hello".logType(); // string
(1).logType(); // number
true.logType(); // boolean
1n.logType(); // bigint
Symbol().logType(); // symbol
(() => {}).logType(); // function
({}).logType(); // object
Or, you can use strict mode as suggested in the comments, as that keeps this as the original primitve:
Object.prototype.logType = function () { "use strict"; console.log(typeof this); }
"Hello".logType(); // string
(1).logType(); // number
true.logType(); // boolean
1n.logType(); // bigint
Symbol().logType(); // symbol
(() => {}).logType(); // function
({}).logType(); // object
When passed as this outside of strict mode, you have the very rare case that you encounter a primitive in their wrapped object form ("boxed"), as instance of String:
You can therefore check if your method was called on a string using this instanceof String instead of typeof this === 'string'. If you want to differentiate between strings and objects that inherit from String, you could use this.constructor === String instead.
To get a "regular" string back (or number for Number, or boolean from Boolean, etc.), you can call this.valueOf()
(This means you could also write typeof this.valueOf() - but note that this may be misleading because any object could return, say, a string from its valueOf method without actually having been a string originally.)
Note: You cannot differentiate between 'abc'.yourMethod() and new String('abc').yourMethod() this way because you get an instance of String either way.
(Also interesting: Seems like for newer types like BigInt or Symbol you cannot manually create such a wrapper instance, new BigInt(1n) will fail. But (function () { return this }).call(1n) would achieve the same, although I have no idea why anybody would want that...)
All that said: The easiest way to get the exact behavior you want (this being the actual string) is by defining your function in a context that is in strict mode:
(function {
'use strict'
Object.prototype.logType = function () { console.log(typeof this); }
})()
Now it will work as intended.

Difference between toJSON() and JSON.Stringify()

if you need to read or clone all of a model’s data attributes, use its
toJSON() method. This method returns a copy of the attributes as an
object (not a JSON string despite its name). (When JSON.stringify() is
passed an object with a toJSON() method, it stringifies the return
value of toJSON() instead of the original object. The examples in the
previous section took advantage of this feature when they called
JSON.stringify() to log model instances.)
http://addyosmani.github.io/backbone-fundamentals/#backbone-basics
Can anyone tell me the difference between both these ways of representing an object in JSON notation. I am just confused whether these to achieve the same or there is a difference.
From the fine manual:
toJSON behavior
If an object being stringified has a property named toJSON whose value is a function, then the toJSON method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON method when called will be serialized.
This is why Backbone uses the toJSON method for serialization and given a model instance called m, you can say things like:
var string = JSON.stringify(m);
and get just the attributes out of m rather than a bunch of noise that your server won't care about.
That said, the main difference is that toJSON produces a value (a number, boolean, object, ...) that gets converted to a JSON string whereas JSON.stringify always produces a string.
The default Backbone toJSON is simply this (for models):
return _.clone(this.attributes);
so m.toJSON() gives you a shallow copy of the model's attributes. If there are arrays or objects as attribute values then you will end unexpected reference sharing. Note that Backbone.Model#clone also suffers from this problem.
If you want to safely clone a model's data then you could send it through JSON.stringify and then JSON.parse to get a deep copy:
var data = JSON.parse(JSON.stringify(model_instance));
var cloned_model = new M(data);
where model_instance is your instance of the Backbone model M.
JSON.stringify() - Any valid JSON representation value can be stringified.
The JSON.stringify(..) utility will automatically omit undefined, function, and symbol values when it comes across them. If such a value is found in an array, that value is replaced by null (so that the array position information isn't altered). If found as a property of an object, that property will simply be excluded.
JSON stringification has the special behavior that if an object value has a toJSON() method defined, this method will be called first to get a value to use for serialization.
toJSON() - to a valid JSON value suitable for stringification.
One example, JSON.stringify() an object with circular reference in it, an error will be thrown. toJSON() can fix it as following.
var o = { };
var a = {
b: 32,
c: o
};
// circular reference
o.d = a;
// JSON.stringify( a ); // an error caused by circular reference
// define toJSON method
a.toJSON = function() {
return { b: this.b };
};
JSON.stringify( a ); // "{"b":32}"
I'm also reading Addy Osmani's Developing backbone.js application, and I have the same question. I figured out by trying his example (the todo list) in the console.
var Todo = Backbone.Model.extend({
defaults:{
title:"",
completed:false
}
});
var todo1 = new Todo();
console.log(todo1.toJSON())
//The console shows
//Object {title: "finish your assignment", completed: false}
console.log(JSON.stringify(todo1))
//The console shows
//{"title":"finish your assignment","completed":false}

Why is this variable returning undefined?

var place = "mundo"["Hola", "Ciao"];
Why does this return undefined? Just because it is garbage?
That is perfectly valid JS, though it doesn't do what you expect.
place is initialized to the 'Ciao' property of String('mundo'). Since it doesn't exist, it is initialized to undefined.
The tricky part:
"Hola","Ciao" is using the comma operator, evaluates "Hola", evaluates "Ciao" and returns "Ciao"
[...] in this case is property access
"mundo"[] "mundo" is converted to a String object to access the property on it.
Proof:
var place = "mundo"["Hola", "toString"];
console.log(place) // function toString() { [native code] }
The array operator on a string object will either try to index into the string and return a specific character from that string (on some JS implementations) or it will try to lookup a property on that object. If the index is a number, some JS implementations (I think this is non-standard behavior) will give you that character from the string.
// returns "m" in Chrome
"mundo"[0]
// returns undefined
"mundo"[9]
But, an array index that isn't a number will try to look for that property on the string object and your particular value won't be found on the string object and thus you get undefined.
// does a property lookup and returns "function toString{[native code]}
"mundo"["toString"]
// returns undefined - no propery named foo
"mundo"["foo"]
So, since there is no property on the string that resembles anything in ["Hola", "Ciao"], you get undefined. Technically, the browser is actually looking for the "Ciao" property when you give it this and because that property doesn't exist, you get undefined.
In a weird test, you can run this code to sort of see what's going on:
var str = new String("mundo");
str["Ciao"] = "Hello";
alert(str["Hola", "Ciao"]); // alerts "Hello"
Working demo of this: http://jsfiddle.net/jfriend00/e6R8a/
This all makes me wonder what in the heck you are actually trying to do that comes up with this odd construct.

Is there a way of specifying what data of an object is passed to the serializer when it's being serialized?

I'm serializing quite complex data-structures using native JSON.stringify of chrome(will switch to some serialization library later for cross-browser support). Most of the data can be looked upon as static, but some is dynamic.
When constructing the data I'd like to be able to do something like
var dynamicString=new String();
{a:"foo", b:dynamicString}
Then set the primitive string-value of dynamicString later on. I'd like to do this rather than changing the property "b" of that object because this would allow for constructing this data a lot easier.
I.e. something like:
I've already found out the primitive value of a String-object can't be changed. I've also found out that the valueOf-function can be set to a custom function, and by returning the desired value in that function this can basically be achieved if doing things like:
var dynamicString=new String("bar");
a.valueOf=function(){return ("foo")};
alert (a+"bar");//foobar
The problem is that the serialization apparently doesn't use the valueOf-function. Serializing the dynamicString-object in this case puts "bar" in the string returned by JSON.stringify, rather than "foo" which is desired.
One way of getting what I'm after is recursively looping though all the objects of the data-structure prior to serialization, and replacing dynamicData-objects by the primitive value they return. But I'd like to avoid that if possible.
Any suggestions?
Edit: I could of course also put an object in the data, which I also have a reference to. Then I can set the data of that object "dynamically", which will be reflected in the result-string. But that adds another level in the structure. I just want a string, not an object with a string-property.
I've also found out that the valueOf-function can be set to a custom function, and by returning the desired value in that function this can basically be achieved if doing things like:
var a = new String("bar");
a.valueOf = function(){return ("foo")};
console.log(a+"bar"); // "foobar"
Yet, if you overwrite valueOf it does not make any sense to use a String object (with some different internal value). Use a plain object with a valueOf function, or even with just a property which is changed dynamically. With your current approach, you're getting
console.log(a.concat("bar")); // "barbar"!
The problem is that the serialization apparently doesn't use the valueOf-function. Serializing the dynamicString-object in this case puts "bar" in the string returned by JSON.stringify, rather than "foo" which is desired.
Yes. JSON.stringify does call the toJSON method on objects, which in your case still returns the internal value. If you overwrite that, it will work:
a.toJSON = a.valueOf;
console.log(JSON.stringify(a)); // '"foo"'
As said above, using native String objects doesn't make much sense. Instead, we should create an extra constructor for them:
function DynamicString(value) {
if (!(this instanceof DynamicString))
return new DynamicString(value);
this.set(value);
}
var p = DynamicString.prototype = Object.create(String.prototype, {
constructor: {value: DynamicString, configurable: true}
});
p.set = function(value) {
this.__value__ = String(value);
};
p.valueOf = p.toString = p.toJSON = function() {
return this.__value__;
};
Now, using it:
var a = new DynamicString("foo");
console.log(a+"bar"); // "foobar"
console.log(JSON.stringify({a:a, b:"bar"})); // '{"a":"foo","b":"bar"}'
You even got all the String.prototype methods on it (yet they will yield primitive strings, not dynamic ones).
Use the 2nd parameter to JSON.stringify: replacer
You can also use the toJSON property, it's behaves like a toString.

Understanding how variables are passed

I just can't understand how variables are passed, why are some passed by reference while other by value?
Example:
var a=4;
var b=a;
b=b++;
alert(a);//unmodified 4
var c=["t","ttt"];
var d=c;
d=d.sort(function(x,y){return (y.length-x.length);});
alert(c);//modified ["ttt","t"]
Where can I find a list of what variables will work like the first example and which like the second? (booleans, strings, etc... there are too many to test them all by miself)
JavaScript is always pass by value.
It's very common to say that objects in JavaScript are pass by reference, however that is not true. In a true pass by reference language, you could pass a reference to an object's reference, and have it point to another object. This is not possible in JavaScript.
Take, for example, C#. By default everything in C# is pass by value, just like JavaScript.
void foo(string s) {
s = "passbyvalue";
}
string a = "original value";
foo(a);
// a is still "original value"
However, if you alter foo to use pass by reference, the behavior changes:
void foo(ref string s) {
s = "passbyreference";
}
string a = "original value";
foo(ref a);
// a is now "passbyreference"
In JavaScript, only the first example is possible.
In JavaScript, everything is pass by value. This includes the object reference (yes, it is confusing). The reference itself is a value (it's very much like a pointer). It merely contains an id that allows the runtime to look up the object that is most likely stored in its heap. When you pass an object to a function, you are actually copying its reference into the function
It may be a seemingly small, and even anal, difference. But it's a key difference. Languages that have true pass by reference such as C# and C++ allow you to do things that are simply not possible in languages like JavaScript. The above trite example is one.
Imagine everything in JavaScript as an object. Object references are copied by value, but object properties are still used by reference. So:
var a = {};
var b = a;
b = 'Blah';
console.log(a); // Still {}
and
var a = {};
var b = a;
b.test = 'Blah';
console.log(a); // Is now {test: 'Blah'}
To simplify things a little: properties are by reference, values are by value.
Usually you can check the method on MDN. For example, the sort method's documentation states (emphasis added):
Sorts the elements of an array in place and returns the array.
The "in place" there says it will modify the array.
All primitives (for example, numbers and booleans) are passed by value; all objects are passed by reference, although some (like String objects) are immutable (although still passed by reference).
All objects get passed by reference and values by value. (technically this is not true, but it explains the behaviour)

Categories

Resources