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.
Related
I'm defining a JavaScript function like this:
function F() {
}
It gets some more properties and methods through a prototype object. When it's time to use the function, I'd like to see the object name, like for Array or other built-in objects. But I always see "Object" as the object type.
var i = new F();
console.log(i);
// Object { }
// wanted: something with "F" instead of "Object"
var a = [];
console.log(a);
// Array []
console.log(document);
// HTMLDocument about:newtab
What can I do to get the desired output?
I found several other questions here but none had the desired effect. I'm not too experienced with the whole prototype and new thing in JavaScript so I don't know the correct terms to find relevant results.
My function F must still be callable as a regular function, so I don't think I am interested in creating a class as supported in newer web browsers.
My code targets any web browsers that's in wide use today, which is probably Chrome, Firefox, Safari and a bunch of Android apps with unknown internals (Samsung?).
The display name of objects is controlled by their .toString() behavior. The default implementation of .toString() uses the value returned by the Symbol.toStringTag property (see here), which will be found on the Object prototype if not defined explicitly for your type. Thus it will have the value "Object" unless you override it.
function F() { }
let i = new F();
console.log(i.toString()); //[object Object]
F.prototype[Symbol.toStringTag] = "F";
console.log(i.toString()); //[object F]
Provided that the object MAY contain own property called "hasOwnProperty":
> a={abc: 123};
{ abc: 123 }
> a.hasOwnProperty("abc");
true
> a['hasOwnProperty'] = 1;
1
> a.hasOwnProperty("abc");
TypeError: a.hasOwnProperty is not a function
...
This works, kinda ugly interface, if you think about Object.keys(), Object.assign() ETC.. So, is there a better way?
> Object.hasOwnProperty.call(a, "abc");
true
> Object.hasOwnProperty.call(a, "hasOwnProperty");
true
And why shouldn't the solution be the only recommended way? Using methods directly from an object seems like a recipe for a failure, especially if it is containing external data (not in one's control)
The appropriate/recommended way to use hasOwnProperty is as a filter, or a means to determine whether an object... well, has that property. Just they way you are using it in your second command a.hasOwnProperty('abc').
By overwriting the Object hasOwnProperty property with a['hasOwnProperty'] = 1, while it's safe and valid, just removes the ability to use the hasOwnProperty function on that Object.
Am I missing your true question here? It seems like you already knew this from your example.
By
'using methods directly from an object seems like a recipe for a failure
are you referring to something like this:
> dog = {speak: function() {console.log('ruff! ruff!')}};
> dog.speak(); // ruff! ruff!
Because that is extremely useful in many ways as you can imagine.
If you can use ECMAScript 2015 you can try Reflect.getOwnPropertyDescriptor.
It returns a property descriptor of the given property if it exists on the object, undefined otherwise.
To simplify you can create this function:
var hasOwnProp = (obj, prop) => Reflect.getOwnPropertyDescriptor(obj, prop) !== undefined;
var obj = new Object();
obj.prop = 'exists';
console.log('Using hasOwnProperty')
console.log('prop: ' + obj.hasOwnProperty('prop'));
console.log('toString: ' + obj.hasOwnProperty('toString'));
console.log('hasOwnProperty: ' + obj.hasOwnProperty('hasOwnProperty'));
var hasOwnProp = (obj, prop) => Reflect.getOwnPropertyDescriptor(obj, prop) !== undefined;
console.log('Using getOwnPropertyDescriptor')
console.log('prop: ' + hasOwnProp(obj, 'prop'));
console.log('toString: ' + hasOwnProp(obj, 'toString'));
console.log('hasOwnProperty: ' + hasOwnProp(obj, 'hasOwnProperty'));
obj['hasOwnProperty'] = 1;
console.log('hasOwnProperty: ' + hasOwnProp(obj, 'hasOwnProperty'));
Any built-in can be overridden in JS - it's generally considered best practice to avoid overriding any native methods where possible. If the original functionality is preserved it's OK as it will still behave as expected and even could possibly extended further if overridden correctly again.
As that's considered best practice I recommend either remapping the keys to avoid overriding them. If remapping the keys is not an option then you can maybe make it feel a little less messy by either locally referencing/wrapping Object.hasOwnProperty or Object.prototype.hasOwnProperty. In the case of hasOwnProperty you could possibly implement an iterator (as iterating over enumerable non-inherited properties is a very common use of hasOwnProperty) method to reduce the likelihood of its use. There's always still the risk of someone less familiar with your object attempting to directly iterate so I really feel that key mapping is the safer bet even if it does cause a slight difference in between server-side keys and local ones.
A key mapping could be as simple as a suffix using hasOwnProperty_data instead of hasOwnProperty this would mean objects would behave as expected and your IDE's autocomplete likely will still be close enough to know what the property represents.
A mapping function might look like the following:
function remapKeys(myObj){
for(var key in myObj){
if(Object.prototype.hasOwnProperty.call(myObj, key)){
if((key in Object) && Object[key] !== myObj[key]){ // Check key is present on Object and that it's different ie an overridden property
myObj[key + "_data"] = myObj[key];
delete myObj[key]; // Remove the key
}
}
}
return myObj; // Alters the object directly so no need to return but safer
}
// Test
var a = {};
a.hasOwnProperty = function(){ return 'overridden'; };
a.otherProp = 'test';
remapKeys(a);
console.log(a); // a { hasOwnProperty_data : function(){ return 'overridden';}, otherProp: 'test' }
console.log(a.hasOwnProperty('otherProp')); // true
I am quoting this statement from the JavaScript Definitive Guide book in section 6.1.4 Object.create(). The following statement doesn't seem clear to me.
You can pass null to create a new object that does not have a prototype, but if you do this, the newly created object will not inherit anything, not even basic methods like toString() (which means it won't work with the + operator either)
var o2 = Object.create(null) // o2 inherits no props or methods.
At this point, I was thinking "Oh Wow". It doesn't inherit any basic methods, when you set Object.create(null). So, I tried to give it a try on console to see if this was really the behavior. I ran the script that is below, and got an unexpected result.
var o2 = Object.create(null);
o2.number = 1;
console.log(o2.number.toString()); // "1"
When I ran this code, I was thinking that the .toString was not going to work. I am bit confused or may not understand how things are working here. I was wondering if anyone could clear things up for me. Any help will be appreciated, and thanks for the time for reading my problem. :)
You're calling toString on the number property, which is not the object itself. If you were to try o2.toString(), it would not work.
toString works in your example because you're running it on a number, not the object itself.
It works because it's no different than this:
var n = 1;
console.log(n.toString());
To see the results of no prototype, try this instead:
var o2 = Object.create(null);
console.log(o2.toString());
When you do ...
o2.number = 1
... you're creating a property named number and adding that property to your o2 object.
When you do ...
o2.number.toString()
... you're executing toString not on o2, but on property o2.number.
If you do...
console.log(typeof o2.number)
... you'll see that o2.number has type number, and thus has all methods associated with numbers.
If you do ...
console.log(typeof o2)
... you'll see that o2 has type object.
If you try executing o2.toString, you'll get an error and see that this object indeed doesn't have any method named toString.
Note :
In my experience, you probably don't ever want to do something like ...
var o2 = Object.create(null);
o2.number = 1;
What you probably want instead, is something like ...
var o2 = Object.create(Object.prototype);
o2.number = 1;
... which can can be written more elegantly like ...
var o2 = {
number : 1
};
There is little to no advantage with creating objects that do not inherit from Object.prototype. And if other people end up using your code, you're likely to confuse the heck out of other developers when they're trying to call eg. hasOwnProperty or toString on your object and they're getting an error (as they these methods expect to exist for any object).
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.
using prototype method we can create new methods... like...
Object.prototype.newMethod=function(){
// do something
}
Here I am defining the newMethod with an anonymous function... now if I want to use this method, I have to use it like: <object>.newMethod();
But now I want to create a new method which I can use like: <object>.newMethod;... no brackets... How can I do that...??
please don't use any jQuery...
Erm, you can't. To call a method, you write parentheses after it. Otherwise you're just referencing it.
The only exception to this rule is when you write something like new Date, where the parentheses are implict due to the new keyword and only because there are no arguments given.
I can't really understand why you would want to do that, but it is possible, albeit with a nasty hacky workaround. What you're actually looking for, AFAIK, is a magic property (like the someArray.length property).
var foo = {val:'foo'};
foo.length = (function(that)
{
return function()
{
return that.val.length;
}
})(foo);
//at this point foo.length(); returns 3, but still requires parentheses
//so, build another closure, and assign a valueOf method to the lenth method:
foo.length.valueOf = (function(method)
{
return function()
{
return method();//call the length method
}
})(foo.length);
console.log(foo.length +1);//logs 4
foo.val += 'bar';
console.log(foo.length);//logs 6
//BUT:: be carefull!!
alert(foo.length);//coerces to string, we haven't redefined the toString method, so the function code will be alerted
alert(foo.length + '');//alerts 6
This is just to show you that, yes it is theoretically possible, but please, please, don't use this kind of overly polluted hacks... I haven't thoroughly tested this, but ATM, I've already noticed that console.log(foo.length); can return a different value, not sure why, yet:
foo = {val:'foo'};
foo.length = (function(that){return function(){ return that.val.length;};})(foo);
foo.length.valueOf = (function(method){return function(){return method();};})(foo.length);
foo.length;//returns 3, great
foo.val += 'bar';
console.log(foo.length);//logged 3 at first, now it's back to logging 6!<-- don't trust this is the conclusion
The only way to call a function without parenthesis would be to define it using getters and setters.
Note these are new to JavaScript 1.8 and are not supported by all browsers.