Perform a `typeof JSON` comparison - javascript

I have a method that receives something and it needs to determine the type of the received value. I can use the typeof thing to perform regular comparisons like if it is a number or a string. But how can I do this for JSON objects? Comparing them with JSON brings up the error:
Uncaught TypeError: Expecting a function in instanceof check, but got #< Object>
So I guess that comparing a JSON object type with JSON is not the way?
The original code is like:
check = (what) ->
if what instanceof JSON
alert "Yooo"
check({compare: "me"})

The type will be object, not JSON. To see what you're working with, you can check if it has the properties you're looking for. Check the length, or if it has specific keys.
Here's a pretty good informational page on working with JSON. JSON in JavaScript

JSON stands for JavaScript Object notation and is simply a name for how object literals are written in JavaScript it is not a type.
var a = {"foo":"My foo","bar" : 4};
var b = {"foo":"My foo","bar" : 0};
var c = {"foo":"My c foo","barella" : -1};
var d = '{"baz":"My baz","bar" : 4}';
a,b,c and d are all objects the first three of type object the fourth of type string. You could from a type theoretic point of view say that the first two have the same type. If you did eval("var e =" + d) then the string would be in d would be evaluated and since d is an object serialized to JSON the result would be a valid object that would be assigned to e.
in other words JSON is no type it's part of the JavaScript grammar, the result of evaluating JSON is an object and the type of that object will vary depending on the object literal. using typeof on such an object will yield "object" (regards less of the type theoretic type of the object).
If you wish to test and object against a specific type you would therefor have to test it for all the expected properties and methods

class JSON
constructor: (#data) ->
get: (key) ->
#data[key]
set: (key, value) ->
#data[key] = value
a = new JSON "foo":"My foo", "bar" : 4
a.get('foo')
a.data.foo
a.data['foo']
console.log(a instanceof JSON)
:D You really shouldn't be doing this though, at least not to create a JSON type. But it's possible that you can create your own wrapper for pretty much anything. Combining this with the Object.defineProperty to setup getters and setters based on #data, you could do some powerful stuff. It doesn't have method_missing methods, but you can achieve similar results with Object.defineProperty

First type of return string, second, there is no such thing as Type JSON in type of possible return values. see the this page for detail
in your case you will receive "object".

Related

JavaScript - Computed properties - deep confusion

Seems I am quite confused by computed properties in JavaScript.
When I define an object and I put [d] as a key (as a property key/name) what does this [d] actually do? Seems that for some values d it calculates s = d.toString() and uses that value s as the property key.
But for other values d (e.g. when d is a symbol) it uses really the symbol's value as the key.
So this dual behavior of [d] (as a syntax construct) seems confusing. Could someone explain in depth how this works?
Are there other special cases btw? Or is it just when d is a Symbol when we have that special behavior?
Back to the basics: what things can be keys/names of properties of an object? Is it just strings or just strings and symbols or is there also something additional... ?
Example:
var symbol = Symbol("test");
function Animal(name){
this.name = name;
}
Animal.prototype = {};
Animal.prototype.constructor = Animal;
function Dog(breed){
this.breed = breed;
this.name = "Dog";
this.s = symbol;
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
console.log("001");
var d = new Dog("Sharo");
for (let x in d){
console.log(x, ":", d[x]);
}
console.log("002");
d = new Object();
for (let x in d){
console.log(x, ":", d[x]);
}
console.log("003");
d = new Number(5);
for (let x in d){
console.log(x, ":", d[x]);
}
var d1 = {};
var d2 = {};
var d = new Dog("Sharo");
var m = {[d1] : 5, [d2] : 10, [d] : 20, z : 100, symbol: 2000, [symbol] : 5000};
console.log("============================");
console.log(m);
for (let x in m){
console.log(x, ":", m[x]);
}
console.log("============================");
Since no one seems interested in answering this question I will answer it myself based on the comments which I got above, and due to which I am not confused anymore.
Note that this answer here is ES6 based. I mean... who knows what else the JavaScript future will hold :)
When I define an object and I put [d] as a key (as a property key/name) what does this [d] actually do? Seems that for some objects d it calculates s = d.toString() and uses that value s as the property key. But for other objects d (e.g. when d is a Symbol) it uses really the Symbol's value as the key.
Yes, that's correct. When d is a Symbol its value is used directly. When d is anything but Symbol its value is coerced to a string and that string is used as the property name/key. The coercion is more like String(d) rather than d.toString().
So this dual behavior of [d] (as a syntax construct) seems confusing. Could someone explain in depth how this works?
Already explained above.
Are there other special cases btw? Or is it just when d is a Symbol when we have that special behavior?
There are no other "special cases". As of ES6 only strings and symbols can be property keys.
Back to the basics: what things can be keys/names of properties of an object? Is it just strings or just strings and symbols or is there also something additional... ?
As already said, as of ES6 only strings and symbols can be property keys.
References:
(1)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
"Property names are string or Symbol. Any other value, including a number, is coerced to a string."
(2)
https://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey
In a comment on another answer you said you thought property keys were always strings. They were, until ES2015. :-)
So this dual behavior of [d] (as a syntax construct) seems confusing. Could someone explain in depth how this works?
As of ES2015, Symbols were added to the language, and object property keys became able to be either strings or Symbols. So when you do:
const obj = {
[d]: "foo"
};
...the computed property key ([d]: "foo") part of that works like this:
Let value be the result of evaluating the expression "foo"
Let keyValue be the result of evaluating the expression d
If keyValue is a Symbol, let key = keyValue; otherwise, let key = String(keyValue)
Set the property key on obj to the value value
I've left out a couple of details in there for clarity. You can see that in the ToPropertyKey abstract operation in the specification, which is used whenever a value is used as a property key (in an object literal as above, or when accessing an object property via brackets notation, etc.).
Are there other special cases btw? Or is it just when d is a Symbol when we have that special behavior?
Back to the basics: what things can be keys/names of properties of an object? Is it just strings or just strings and symbols or is there also something additional... ?
Just Symbol and string. :-) It's not so much that Symbol is a special case, it's just that where it used to be that property keys were always strings, now they can be strings or Symbols.
(Fun fact: In the spec, they define "property key" as the string or Symbol that identifies a property, and "property name" as a property key that's a string. But don't rely on it, the spec itself is a bit inconsistent, and the Object.keys method — which returns an array of property names, not property keys — existed before that terminology was added in ES2015. And then they added a keys method to arrays that returns an iterator of numbers [the indexes in the array]. Fun fun fun... :-) )
All ES5 and earlier operations that returned or looped over property names were specified in ES2015 to ignore Symbol-keyed properties. So for-in, Object.keys, Object.getOwnPropertyNames all only look at the string-keyed properties. ES2015 added Reflect.ownKeys (which includes both strings and Symbols) and Object.getOwnPropertySymbols (which includes only own, Symbol-keyed properties).
Side note:
Seems that for some objects d it calculates s = d.toString() and uses that value s as the property key...
Not just objects, and it's more like String(d) (although if d is an object it comes to the same thing). Anything that isn't string or Symbol is converted to string.
...But for other objects d (e.g. when d is a Symbol) it uses really the Symbol's value as the key.
Symbols aren't objects, Symbol is a primitive type. But yes, if the property key is a Symbol, it's used directly, not converted to string.
I thought I'd test out a minor point from TJ's answer:
[I]t's more like String(d) (although if d is an object it comes to the same thing)
sessionStorage.setItem("secret", "sssh!");
const evilObject = {
toString() {
console.log("I stole a secret: " + sessionStorage.getItem("secret"));
return "InnocentPropertyName";
}
};
const obj = {
[evilObject]: "Hello! I'm the property's value."
}
console.log(obj.InnocentPropertyName)
If you check the console after this you end up with:
I stole a secret: sssh!
Hello! I'm the property's value.
Conclusion: If toString() is defined on a non-string object, and you pass it to the computed property square bracket operator, then toString() will be called when setting up that property. Furthermore, said toString() may have side-effects, and potentially evil ones at that, as demonstrated.
Key security aside: If you are creating properties using this operator, make sure you trust whatever you put into it. That applies even if you think what you're putting in is a string. If you got that "string" from elsewhere, can you be really sure it's a string? If you expect a string, then sanitize it: do a dynamic test for the string type before using it.

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}

adding properties to primitive data types other than Array

I'm not supposed to add elements to an array like this:
var b = [];
b.val_1 = "a";
b.val_2 = "b";
b.val_3 = "c";
I can't use native array methods and why not just an object. I'm just adding properties to the array, not elements. I suppose this makes them parallel to the length property. Though trying to reset length (b.length = "a string") gets Uncaught RangeError: Invalid array length.
In any case, I can still see the properties I've set like this:
console.log(b); //[val_1: "a", val_2: "b", val_3: "c"]
I can access it using the dot syntax:
console.log(b.val_1); //a
If an array is just an object in the same way a string or a number is an object, why can't (not that I'd want to) I attach properties to them with this syntax:
var num = 1;
num.prop_1 = "a string";
console.log(num); //1
I cannot access its properties using dot syntax
console.log(num.prp); //undefined
Why can this be done with array and not with other datatypes. For all cases, I should use {} and would only ever need to use {}, so why have arrays got this ability?
JSBIN
Because arrays are treated as Objects by the language, you can see this by typing the following line of code:
console.log(typeof []) // object
But other types like number literals, string literals NaN ... etc are primitive types and are only wrapped in their object reprsentation in certain contexts defined by the language.
If you want to add properties or methods to a number like that, then you can use the Number constructor like this:
var num = new Number(1);
num.prop_1 = "fdadsf";
console.log(num.prop_1);
Using the Number constructor returns a number object which you can see by typing the following line:
console.log(typeof num); // object
While in the first case:
var num = 1;
console.log(typeof num) // number
EDIT 2: When you invoke a method on a number literal or string literal for instance, then that primitive is wrapped into its object representation automatically by the language for the method call to take place, for example:
var num = 3;
console.log(num.toFixed(3)); // 3.000
Here num is a primitive variable, but when you call the toFixed() metohd on it, it gets wrapped to a Number object so the method call can take place.
EDIT: In the first case, you created a string like this first var str = new String(), but then you changed it to str = "asdf" and then assigned the property str.var_1 = "1234".
Of course, this won't work, because when you assigned str = "asdf", str became a primitive type and the Object instance that was originally created is now gone, and you can't add properties to primitives.
In the second, it didn't output undefined like you said, I tested it in Firebug and everything worked correctly.
EDIT 3:
String literals (denoted by double or single quotes) and strings returned from String calls in a non-constructor context (i.e., without using the new keyword) are primitive strings.
This is taken from MDN Documentation, when you use string like that var p = String(3) it becomes a conversion function and not a constructor and it returns a primitive string as you can see from the quote above.
Regarding your second comment, I didn't understand how my comment has been defied, because if you try to console.log(p.pr) you'll get undefined which proves p is a primitive type and not an object.
If an array is just an object in the same way a string or a number is an object,
An array is different than strings, numbers, booleans, null and undefined. An array IS an object, while the others in the list are primitive values. You can add properties to the array just like you would with any other object, anything different being just what makes arrays special (the length property you mentioned for example). You cannot add properties or call methods on primitive values.
In this previous answer i talked about the use of Object wrappers over primitive values. Feel free to read it to see more about Object wrappers. The following will be a very short example:
console.log('TEST'.toLowerCase()) // 'test'
While it may seem that the toLowerCase method is called on the string 'TEST', in fact the string is converted automatically to a String object and the toLowerCase method is called on the String object.
Each time a property of the string, number or boolean is called, a new Object wrapper of the apropriate type is created to get or set that value (setting properties might be optimized away entirely by the browser, i am not sure about that).
As per your example:
var num = 1; // primitive value
num.prop_1 = "a string"; // num is converted to a Number, the prop_1 property is set on the Object which is discarded immediately afterwards
console.log(num); //1 // primitive value
console.log(num.prp); // num is converted to a Number, which doesn't have the prp property
My example:
Number.prototype.myProp = "works!";
String.prototype.myFunc = function() { return 'Test ' + this.valueOf() };
Boolean.prototype.myTest = "Done";
console.log(true.myTest); // 'Done'
console.log('really works!'.myFunc()); // 'Test really works!'
var x = 3;
console.log(x.myProp.myFunc()); // 'Test works!'
console.log(3['myProp']); // 'works!'
On the other hand:
console.log(3.myProp); // SyntaxError: Unexpected token ILLEGAL
The number isn't necessarily treated differently, that syntax just confuses the parser. The following example should work:
console.log(3.0.myProp); // 'works!'

what is the equivalent JavaScript function for VBScript CreateObject()

What is the JavaScript equivalent function for CreateObject("Scripting.Dictionary")?
I have to convert following two statements from VBScript to JavaScript, anyone can help me to find a solution.
Set oInvoicesToCreate = CreateObject("Scripting.Dictionary")
If Not oInvoicesToCreate.Exists(cInvoiceID) Then
oInvoicesToCreate(CStr(cInvoiceID)) = ""
End If
var oInvoicesToCreate = {};
if(oInvoicesToCreate[cInvoiceID] === undefined){
oInvoicesToCreate[cInvoiceID] = "";
}
You probably don't want to check the hasOwnProperty method because you'll want to check if anything in the prototype chain has that property as well and not overwrite it. checking with the []s will let you know if any property on any prototype items have the property as well.
As bluetoft says in this answer, in Javascript you can use a plain object instead. However, there are a few differences between them that you should be aware of:
First, a Dictionary's keys can be any type:
var dict = new ActiveXObject('Scripting.Dictionary');
dict(5) = 'Athens';
console.log(dict('5')); //prints undefined
whereas any value used for a Javascript object's key will be converted to a string first:
var obj = {};
obj[5] = 'Athens';
console.log(obj['5']); // prints 'Athens'
From MDN:
Please note that all keys in the square bracket notation are converted to String type, since objects in JavaScript can only have String type as key type. For example, in the above code, when the key obj is added to the myObj, JavaScript will call the obj.toString() method, and use this result string as the new key.
Second, it is possible to set a Dictionary to treat differently cased keys as the same key, using the CompareMode property:
var dict = new ActiveXObject('Scripting.Dictionary');
dict.CompareMode = 1;
dict('a') = 'Athens';
console.log(dict('A')); // prints 'Athens'
Javascript key access via [] doesn't support this, and if you want to treat differently-cased keys as the same, you'll have to convert the potential key to lowercase or uppercase before each read or write.
For your specific scenario, neither of these differences matter, because the keys are numeric strings (1) which have no case (2).

Javascript - .toJSON

I am a newbie to JSON & hence I am not sure what $.toJSON(params) means.
Please explain what this does.
It could be this jQuery plugin
var myObj = {};
myObj.propA = "a";
myObj.propB = "b";
myObj.propC = "c";
var jsonString = $.toJSON(myObj); // same as jQuery.toJSON(myObj)
// output: '{ "propA" : "a", "propB" : "b", "propC" : "c" }'
See: http://www.json.org/js.html
A JSON stringifier goes in the opposite direction, converting JavaScript data structures into JSON text. JSON does not support cyclic data structures, so be careful to not give cyclical structures to the JSON stringifier.
var myJSONText = JSON.stringify(myObject, replacer);
If the stringify method sees an object that contains a toJSON method, it calls that method, and stringifies the value returned. This allows an object to determine its own JSON representation.
The stringifier method can take an optional array of strings. These strings are used to select the properties that will be included in the JSON text.
The stringifier method can take an optional replacer function. It will be called after the toJSON method (if there is one) on each of the values in the structure. It will be passed each key and value as parameters, and this will be bound to object holding the key. The value returned will be stringified.
So if you have a $.toJSON() method, it could be a badly implemented function to "stringify", or it could be a method that returns the "JSON Representation" of $
It passes the variable params as an argument to the method named toJSON attached to the object stored in the (unhelpfully named) variable $.
Based on the name, it probably converts the contents of the params variable to a String formatted according to the JSON specification.

Categories

Resources