How to force toString when logging object in javascript - javascript

I wonder if it is posible to get "content here" as a response (for example a log) from this code:
function Obj () {
this.toString = function(){ return "content here" };
}
var obj = new Obj;
console.log(obj);
I know I can force it with String(), toString() and ""+obj, but I want to know if there is a way of forcing it from WITHIN the object.

Your edit adding
I know I can force it with String(), toString() and ""+obj, but I want to know if there is a way of forcing it from WITHIN the object.
...changes the question. The simple answer is "no, you can't do that within the object." In order for the toString on your object to be called, something needs to say "I want the primitive form of this" (or specifically "I want the string form of this"). console.log doesn't do that, it provides richer information than that.
Your putting a toString on your object means that any time it's converted to a string, your function will get called, but it doesn't dictate when that happens. You can also use valueOf. There's more about this in the spec: §9.1 - ToPrimitive, §8.12.8 - [[DefaultValue]] (hint), and §9.8 - ToString.
But adding toString (or valueOf) doesn't let you dictate when it happens; you can't, that's just done by the rules of JavaScript or the calling code doing it (explicitly, or implicitly).
Original Answer:
The simplest way is to use String on it:
console.log(String(obj));
You could add your own method:
console.logString = function(s) {
console.log(String(s));
};
You could probably alter log:
var old = console.log;
console.log = function(s) {
var a = Array.prototype.map.call(arguments, function(a) {
return String(a);
};
return old.apply(console, a);
};
...but I would not recommend it.

Related

Using both `get` and `apply` Proxy traps on plain JS object

The following code is a simple proxy that logs out the "gets" that were trapped:
var p = new Proxy({}, {
get: function(target, property, receiver) {
console.log("getting: ", property);
return target[property];
}
});
When I coerce this into a String with "hello " + p, I get the following output in the console:
getting: Symbol(Symbol.toPrimitive)
getting: valueOf
getting: toString
getting: Symbol(Symbol.toStringTag)
"hello [object Object]"
Everything is fine so far, but let's do something a little sneaky and proxy a function, but actually still use it as a proxy to our plain object we used in the last example. The reason I want this is because I'd like to be able to capture both gets and applys on this obj.
Notice the return target.obj part - we're really using this to proxy obj still - it's just that we're doing it via fn:
var fn = function(){};
fn.obj = {};
var p = new Proxy(fn, {
get: function(target, property, receiver) {
console.log("getting: ", property);
return target.obj[property];
}
});
Now, I'd have thought this would produce exactly the same output as the last example for "hello " + p, but I was wrong:
getting: Symbol(Symbol.toPrimitive)
getting: valueOf
getting: toString
getting: Symbol(Symbol.toStringTag)
"hello [object Function]"
Notice that it has resulted in a Function string tag rather than an Object one. What's going on here? It's as if toString is being called on fn rather than obj. (Edit: But we can add fn.toString = function(){ return "fn"; } and it doesn't change the output, so maybe it's not fn that is being stringified here?)
If you pop a debugger statement in there, you'll see it's actually returning fn.obj.toString as you'd expect, but for some reason the final output is a function rather than an object (though I'm not entirely sure which function). Thanks for your help!
P.S. I haven't explained the full context of my use case (short version: it's for a DSL, so bending "good practice" is fine), and so suggesting alternative patterns to achieve both get and apply traps on an object (in effect) may not be relevant to my particular case. I'd really just like to understand why the above approach isn't working like I expect it to, but would also like to ensure the question is broad enough to help future readers in a similar situation.
I think I've found the bug. When we return a function, it looks like we need to bind it to target.obj, otherwise it's being bound to some function somewhere. I'm not completely up to scratch on this stuff, but I think it makes sense. So here's the updated, working code:
var fn = function(){};
fn.obj = {};
fn.toString = function(){ return "fn"; }
var p = new Proxy(fn, {
get: function(target, property, receiver) {
console.log("getting: ", property);
let result = target.obj[property];
if(typeof result === 'function') {
result = result.bind(target.obj);
}
return result;
}
});

Get a more useful value than "[object Object]" for debugging?

Couldn't there a be a environment flag in JavaScript so you could turn on some metadata for objects.
So instead when you are debugging and get:
[object Object]
you would get the variable name and type:
[foo String]
why isn't this possible?
JSON.stringify might be what you are looking for, though it won't give you the name of the variable – JavaScript simply can't do that without 3rd party tools.
The constructor function of your object can be reached by using its constructor property, though there's no guarantee with this as the constructor property is writable.
You might also want to look into the debugger statement.
A bit hacky , but it can help you to find what is your object source :
function Foo()
{}
var toClass = function(a)
{
var _name = a.constructor.toString().match(/^function (\w+)/i); //\w probably should be enhanced
console.log(_name[1])
}
toClass( new Foo()) //Foo
toClass( [1, 2]) //Array
toClass( new Date()) //Date
toClass( {"a":2}) //Object
Aside note : don't override toString just for debugging. toString has its purpose. and should be used as it was meant to be used.
To directly answer your question about just flipping a "global flag" instead of changing your debugging methodology:
Assuming you'd only do this during debugging, you can temporarily override the Object.prototype.toString to return a JSON representation of objects:
Object.prototype.toString = function () { return JSON.stringify(this); };
Then in the browser console:
var obj = { a: 42 };
console.log('My object: ' + obj);
Will give you:
My object: {"a":42}
Even if this answers your question, I don't recommend a global override of a base method because it has the potential to cause catastrophic issues. Try relying on unit tests and breakpoints + debugging as others have suggested in comments.

How to make a good API set using Javascript with input and output parameters?

In the C++ world, we often have this kind of API design pattern:
bool foo(int input1, int input2, int& output1, int& output2, ClassA& outputObj);
The function has a set of input parameters as well as output parameters. The return value indicates if the function succeeds or fails.
An example of this API is:
int input1=0;
int input2=1;
int output1=0;
int output2=1;
ClassA a = new ClassA(...);
bool r = foo(input1, input2, output1, output2, a);
However, in the JavaScript, we have no simple way to simulate "change the primitive argument by reference" behavior. If we wrap the primitive type (number, string etc.) using objects. The API will be ugly.
Any idea about how to design an elegant API using JavaScript with input parameters, output parameters, return values? There might be objects and primitive types in the parameters.
Don't pass output variables as parameters. Rather return them as an array:
function foo(input1, input2) {
...
return [output1, output2, outputObj];
}
You may also return a more descriptive object, for example:
function foo(input1, input2) {
...
return {importantStuff: output1, somethingElse: output2, completeObject: outputObj};
}
Use throw to indicate error.
Bottom line: the way it's done in C++ is not necessarily the way it should be done in Javascript.
You seem to be expecting Javascript to have references (&), like C++. In Javascript, every object is a reference and these are passed by value.
So, if you have:
var foo = {prop: 'foo'};
function f(x) {
x.prop = 'bar';
}
f(foo);
console.log(foo); // Will display {prop: 'bar'}.
In C++ parlance, objects are passed by reference. Read this for full understanding.

Perform omit on the original object

I am very new to underscore js, I am trying to omit a certain property on an Object. What I did was
myObj = _.omit(myObj,name)
console.log(myObj);
Still the myObj seems to have the property name. Although if I do this it seemes to work
newMyObj= _.omit(myObj,name)
console.log (newMyObj)
it seemed to work fine. What am I doing wrong, can someone help? Ok, so myObj looks like this
Angola: "4.134137685",Brunei: "2.532726835",Countries: "2004",Croatia: "1.717672961", keys: Array[11]
I am trying to omit "keys" which again is an array of objects
Thanks
There are these things called "debuggers". If you don't know what they are, then stop everything you're doing and learn about them now. Search Google for "Chrome devtools", for instance. Stop your code (put a breakpoint) at the point before the call to _.omit. In the console, type in myObj to see exactly what it contains, then also name. Or, you could use the 'Scope Variables" section of devtools to check the value of these variables. Now, make a single step (F10). See if or how the variables have changed, or type myObj again into the console to check its value.
In your particular case, you report that the deletion of the property occurs properly when you do
newMyObj= _.omit(myObj,name)
but not with
myObj= _.omit(myObj,name)
In and of itself, that behavior is completely unexplainable. So there's something else going on that you're not telling us about. My guess is that you are doing something like this:
myObj = { keys: [] };
name = "keys";
delete_property();
console.log(myObj.keys); // []
function delete_property(myObj) {
myObj = _.omit (myObj, name);
}
However, this does not do what you might think. The assignment to myObj within the function does nothing; it just reassigns the value of the function argument. It has no effect on the myObj outside the function.
To be sure, we'd need to see more of your actual code, but this is just a regular old debugging problem of the sort you will encounter thousands of times in your programming career, so you're better off learning to solve it yourself.
I interpreted this question to mean you simply want to remove a property from an object using omit(). Note that omit() returns a copy of the object sans the specified property to remove. The method does not alter the object in place.
Given this premise, the code below, which matches what you have, works just fine:
var copy,
obj = {
Angola: "4.134137685",
Brunei: "2.532726835",
Countries: "2004",
Croatia: "1.717672961",
keys: Array[11]
},
check = function (o) {
_.each(o, function (value, key) {
console.log("key: " + key + " value: " + value);
});
};
copy = _.omit(obj, "keys");
check(copy);
obj = _.omit(obj, "keys");
check(obj);
You will get the same result whether you are using a new variable or the existing one.

a String.prototype's "this" doesn't return a string?

What is going on here? Just when I thought I knew JS inside and out, this gem comes up.
String.prototype.doNothing = function() {
return this;
};
alert(typeof 'foo'.doNothing()) // object
alert(typeof 'foo') // string
http://jsfiddle.net/dJBmf/
This is breaking some things that expect a string, such as jQuery's .text(str) method.
To make sure you're always getting a string, trying using this code:
String.prototype.doNothing = function() {
return this.toString();
};
alert(typeof 'foo'.doNothing())
alert(typeof 'foo')
In your original code, this is being returned as the string object and not the actual string.
Here's a thorough overview of the this keyword. Basically, JavaScript converts it into an object, if it wasn't one.
The following steps are performed when
control enters the execution context
for function code contained in
function object F, a caller provided
thisValue, and a caller provided
argumentsList:
If the function code is strict code, set the ThisBinding to
thisValue.
Else if thisValue is null or undefined, set the ThisBinding to the
global object.
Else if Type(thisValue) is not Object, set the ThisBinding to
ToObject(thisValue).
Else set the ThisBinding to thisValue
Same thing happens to Numbers and Booleans. A similar DoNothing function would return a type of object.
Run your code in strict mode to get your expected result!
It's again the difference between string literals and strings, i believe? I once had a question answered here in SO: Property value of a String object in JavaScript
You could've also used the constructor property:
'foo'.constructor === String; //=>true
'foo'.doNothing().constructor === String; //=>true
See also this SO question and this jsFiddle
If String.prototype.doNothing() breaks stuff expecting a string value, I would use return String(this) or this.toString() (this.valueOf() also works here) indeed.
try the following:
return this + '';
function print() {
let str = this;
console.log(str);
}
String.prototype.print = print;
let a = "hello";
a.print();//output:"hello"
To get a better understanding of what's going on try using console logs, like so:
String.prototype.doNothing = function() {
console.log(this);
return this;
};
console.log(typeof 'foo'.doNothing());
console.log(typeof 'foo');
These are the results I get in Firefox:
foo { 0="f", 1="o", more...}
object
string
So it seems in the prototype a string is represented as an object/an array of characters (which does make sense.)
Whether or not you should use toString (as suggested by McHerbie) or casting as a type String (as suggested by mellamokb) depends, in my opinion, on what you plan to do with this value. I would personally lean toward casting it as a String.

Categories

Resources