Why can't you stringify a function expression? - javascript

Why doesn't this produce anything?
console.log(JSON.stringify(function(){console.log('foobar');}));

JSON can't stringify functions at all, it handles them just like undefined or null values. You can check the exact algorithm at EcmaScript 5.1 ยง15.12.3, see also the description at MDN.
However you of course can stringify function expression by casting them to a string, try
console.log("" + function(){console.log('foobar');})

yourFunctionName.toString(); will also stringify a function

JSON has no means to represent a function. It is a data format designed for simplicity and compatibility across languages (and a function is the last thing that will be cross-language compatible).
From the docs for JSON.stringify:
If undefined, a function, or an XML value is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array).

If you want to use JSON.stringify to also convert functions and native objects you can pass a converter function as the second argument:
const data = {
fn: function(){}
}
function converter(key, val) {
if (typeof val === 'function' || val && val.constructor === RegExp) {
return String(val)
}
return val
}
console.log(JSON.stringify(data, converter, 2))
Return undefined from the converter function if you want to omit the result.
The third parameter is how many spaces you want the output to indent (optional).

You cannot do that, but there are some third party libraries can help you do that, like: https://www.npmjs.com/package/json-fn

There are couple of ways to do this.
Let's say you have function foo
> function (foo) { return foo}
if you console log it it returns function name with type
> console.log(foo)
[Function: foo]
when it comes to get access to stringified version of it, you can use one of the ways listed below.
> console.log(`${foo}`)
function (bar) { return bar}
undefined
> console.log(foo.toString())
function (bar) { return bar}
undefined
> console.log("" + foo)
function (bar) { return bar}
undefined

Well there are two ways I know of doing this, first is just String(function) and you can just do eval() on what that returns. There's also a way to run the code in the function directly with regex, it would look something like this:
String(function).replace(/\w+\s{1}\w+\(\)\s?\{(\n\s+)?|\(\)\s?=>\s?{(\n\s+)?/, '').replace(/\n?\}/, '')
with the regex example when you do eval() it runs the code from the function. For both examples where it says "function" put in the name of your function with no () at the end.

Related

Function exists but "undefined". Considered as true when used in condition

Can someone explain to me what is happening in the following code? Thanks.
let myObject = {
myFunction() {
console.log('HELLO WORLD');
}
};
console.log(JSON.stringify(myObject.myFunction));
if (myObject.myFunction) {
console.log('myFunction exists');
}
As described over in MDN, JSON.stringify returns undefined when a function is passed as an argument:
undefined, Functions, and Symbols are not valid JSON values. If any
such values are encountered during conversion they are either omitted
(when found in an object) or changed to null (when found in an array).
JSON.stringify() can return undefined when passing in "pure" values
like JSON.stringify(function(){}) or JSON.stringify(undefined).
Because JSON.stringify() cannot be used on functions, they are treated as undefined. See the explanation here.

SyntaxError when extending Number object

I am trying to extend the Number object with this code:
Number.prototype.isNumber = function(i){
if(arguments.length === 1){
return !isNaN(parseFloat(i)) && isFinite(i);
} else {
return !isNaN(parseFloat(this)) && isFinite(this);
}
}
try {
var x = 8.isNumber();
} catch(err) {
console.log(err);
}
I get SyntaxError: identifier starts immediately after numeric literal
also when I try the following:
Number.isNumber(8)
I get Number.isNumber is not a function!!
The JavaScript parser reads 8.isNumber as a number literal.
To access a Number method on a numeric literal you'll have to surround the number with parenthesis so the JavaScript interpreter knows you're trying to use the number properties.
Number.prototype.isNumber = function(i) {
if (arguments.length === 1) {
return !isNaN(parseFloat(i)) && isFinite(i);
}
return !isNaN(parseFloat(this)) && isFinite(this);
}
try {
var x = (8).isNumber();
console.log(x);
} catch(err) {
console.log(err);
}
I couldn't help it but provide an additional answer although you already accepted one.
The first thing you need to know, is that there is a fundamental difference between the Number object, and the Number prototype (see here).
As it stands, you are extending the Number prototype, not the object itself! Your isNumber implementation actually has the same effect like the following:
Number.prototype.isNumber = function(){return isFinite(this)}
Why? Because in order to execute this prototype method, the parser first needs to know the type of the literal you are invoking the function on. That's why you either need to turn your number literal into an expression by wrapping it in parentheses: (8).isNumber() or by using an even weirder notation 8..isNumber() (the first . is the decimal point, the second the property accessor). At this point, the javascript engine already evaluated it as a Number and thus can execute the isNumber() method.
On the other hand, although at first glimpse your code looks like it could handle the following case correctly (since you are doing a parseFloat): "8".isNumber() will always throw an exception, because here we have a string literal, and the String prototype does not have the according method. This means, you will never be able to detect numbers that are actually string literals in the first place.
What you instead should do, is directly extend the Number object so you can actually do a proper check without having to deal with errors:
Number.isFiniteNumber = function(i){
return !Number.isNaN(i) && Number.isFinite(i);
}
Number.isFiniteNumber(8); // returns true
Number.isFiniteNumber("3.141"); // returns true
Number.isFiniteNumber(".2e-34"); // returns true
Number.isFiniteNumber(Infinity); // returns false
// just for informational purposes
typeof Infinity === "number" // is true
Bonus material:
Extending native objects is potentially dangerous.
Number.isNaN() probably does not what you think it does.

NewBee on NodeJs or PhantomJS: Function names on Javascript

I am just learning NodeJS and/or PhantonJS.
As a programmer with a lot of C experience, I do not like the way NodeJs code is written and find it a bit messy/unreadable. (Sorry if I ruffled any feathers)
In spirit of cleaning up the code, I was trying to do this and found a block.
In C or C++, we should be able to pass a function by name but in NodeJS/PhantomJS it does not seem to work.
Am I doing somthing wrong ?
Can someone explain to me how this is looked at by the Javascript interpreter ?
var page = require('webpage').create();
var printS = function (s) {
console.log(s);
phantom.exit();
}
/* This works */
page.open('http://net.tutsplus.com', function (s) {
console.log(s);
phantom.exit();
});
/* This does not work
page.open('http://net.tutsplus.com', printS(status));
*/
/*But this works
page.open('http://net.tutsplus.com', function (s) { printS(s);} );
*/
page.open('http://net.tutsplus.com', printS(status));
fails because you're not passing the function but rather the result of invoking the function on status. If you want to pass the function, you'd do it this way
page.open('http://net.tutsplus.com', printS);
I thought it might be helpful to have a more extensive explanation. Let's start simple:
In JavaScript, we have values and variables. Variables are containers for values. Almost everywhere where we can use values, we can use variables.
In JavaScript source code, we express values through literals, e.g. the number literal 42. We can directly pass that value to a function:
f(42);
Additionally, instead of passing the value directly, we can pass a variable to the function:
var v = 42;
f(v);
That is, we can substitute values with variables.
Lets consider
var printS = function() { ... };
This clearly is a variable whose value is a function. If we'd directly pass that value to a function (i.e. we pass a function to a function), it would look like:
f(function() { ... }); // similar to f(42)
That's exactly what you have in your first case:
page.open('http://net.tutsplus.com', function (s) {
// ...
});
Since we know that we can replace values with variables, we can just substitute function() { ... } with printS:
var printS = function() { ... }; // similar to var v = 42;
f(printS); // similar to f(v)
So your example would become
page.open('http://net.tutsplus.com', printS);
What is wrong with
page.open('http://net.tutsplus.com', printS(status));
then?
Notice that you added additional characters after printS, namely (status). They don't appear in the your first example where you inlined the function:
page.open('http://net.tutsplus.com', function (s) {
// ...
});
There is no (status) here. Hence these two constructs cannot be not equivalent.
page.open accepts a function value as second argument, but printS(status) doesn't evaluate to the function printS, it calls the function printS and passes the return value to page.open.
Why does
page.open('http://net.tutsplus.com', function (s) { printS(s);} );
work?
Lets remove the content and the argument of the function, and it becomes:
page.open('http://net.tutsplus.com', function () { ... } );
That looks exactly like one of the examples above. function () { ... }, is a function literal, so to speak. It creates a function value. There are no (...) after it which would call the function.
This doesn't work as you hope because page.open wants a function as its second argument... this callback pattern is very common in JavaScript. In your doesn't-work example, printS is being called with status as its argument, and it returns undefined. As undefined is not a function, it doesn't behave as you wish.
In your browser console or the node repl:
> printS = function (s) { console.log(s); };
function (s) { console.log(s); }
> typeof printS('hi');
hi
"undefined"
> typeof function (s) { printS(s); };
"function"
Another thing to know about JavaScript is that its dynamic typing and fairly generous type coercion can result in baffling behavior with no helpful errors to point you towards the root cause of your problem. A debugger or copious use of console.log() is frequently helpful in understanding these sort of problems.

JavaScript: alert object name as a string

I'm trying to alert any JavaScript object as a string, in a function. This means if the parameter given to the function is window.document, the actual object, it should alert "window.document" (without quotes) as a literal string.
The following calls...
example(window);
example(window.document);
example(document.getElementById('something'));
...calling this function...
function example(o) {/* A little help here please? */}
...should output the following strings...
window
window.document
document.getElementById('something')
I've attempted to do this with combinations of toString() and eval() among some more miscellaneous shots in the dark without success.
No need insane backwards compatibility, newer ECMAScript / JavaScript features/functions are fine. Feel free to inquire for clarifications though the goal should be pretty straight forward.
This is not possible to do in a self contained script.
If using a preprocessor would be an option, then you could write one which converts example(whatever) into example('whatever'). Other than that I'm afraid you're out of luck.
The first problem is that objects don't have names.
The second problem is that from your examples, you're not really wanting to print the (nonexistent) name of an object, you want to print the expression that evaluated into a reference to an object. That's what you're trying to do in this example:
example(document.getElementById('something'));
For that to print document.getElementById('something'), JavaScript would have had to keep the actual text of that expression somewhere that it would make available to you. But it doesn't do that. It merely evaluates the parsed and compiled expression without reference to the original text of the expression.
If you were willing to quote the argument to example(), then of course it would be trivial:
example( "document.getElementById('something')" );
Obviously in this case you could either print the string directly, or eval() it to get the result of the expression.
OTOH, if you want to try a real hack, here's a trick you could use in some very limited circumstances:
function example( value ) {
var code = arguments.callee.caller.toString();
var match = code.match( /example\s*\(\s*(.*)\s*\)/ );
console.log( match && match[1] );
}
function test() {
var a = (1);
example( document.getElementById('body') );
var b = (2);
}
test();
This will print what you wanted:
document.getElementById('body')
(The assignments to a and b in the test() function are just there to verify that the regular expression isn't picking up too much code.)
But this will fail if there's more than one call to example() in the calling function, or if that call is split across more than one line. Also, arguments.callee.caller has been deprecated for some time but is still supported by most browsers as long as you're not in strict mode. I suppose this hack could be useful for some kind of debugging purposes though.
Don't know why you need this, but you can try walking the object tree recursively and compare its nodes with your argument:
function objectName(x) {
function search(x, context, path) {
if(x === context)
return path;
if(typeof context != "object" || seen.indexOf(context) >= 0)
return;
seen.push(context);
for(var p in context) {
var q = search(x, context[p], path + "." + p);
if(q)
return q;
}
}
var seen = [];
return search(x, window, "window");
}
Example:
console.log(objectName(document.body))
prints for me
window.document.activeElement

Checking if an argument a string

I have a JavaScript function that uses document.getElementById(). I want to upgrade it to be able to use jQuery selectors ($(this).parent().find("blah")), however it needs to be able to use the original method for backwards compatibility. Is there a way I can test if the argument passed to the function is a string (so I can use getElementById) or a jQuery object (not a string).
I could use .length, but is this a failsafe method of determining whether the argument is a string?
As long as I can test for strings, the jQuery branch can just go in an else - I don't need to make absolutely sure it's not a string, although it would be nice to test if it's jQuery too.
Thanks,
James
following code returns true:
"somestring".constructor == String
Object.prototype.toString.call(your_argument) == "[object String]"
Is this what you're after?
var str = "blah";
if (typeof str == "string") {
} else {
}
And length is definitely not the way to go. Arrays will also have a length property, not to mention any custom object could as well.
I think instanceOf is what you are looking for. See this post: What is the instanceof operator in JavaScript?
JavaScript has two kinds of strings. This checks for both kinds.
function isString (str)
{
return str instanceof String || typeof str === 'string';
}
This will throw always a ReferenceError, if the argument is undeclared although typeof would not throw an error, because JavaScript evaluates from left to right (see Short-circuit evaluation).

Categories