Javascript complicated valueOf method [duplicate] - javascript

This question already has answers here:
Object.prototype.valueOf() method
(2 answers)
Closed 9 years ago.
What does ({}).valueOf.call(myvar) do?
it converts any value to an object (an object remains unchanged, a primitive is converted to an instance of a wrapper type).
My question is how?Can someone give The longer answer how this is done behind the scene.Since valueOf() method is meant to return primitive values not object .
console.log{name:"sameer"}.valueOf() //returns an object but cant be displayed since toString() method will be called by js so [object Object] gets displayed which is a string ,how to display the exact return value from valueOf() method .Is there a way?

Hello again! Once more, we face the mighty opponent. Before we begin, let's dispel one false thought:
valueOf() method is meant to return primitive values not object .
Not accurate. valueOf returns an object if a primitive value was passed to it. If you do valueOf(object), you'd get the same object: valueOf(object) === object. You can trivially see that:
var obj = {};
obj.valueOf() === obj; //true
Now, for the more interesting question: How is valueOf defined? Let's look at the ES5 specification along with the v8 and spidermonkey sources.
valueOf (spec, v8, spidermonkey):
function ObjectValueOf() {
return ToObject(this);
}
As we can see, it simply returns ToObject, as defined in the spec. The rabbit hole emerges.
ToObject (spec, v8, spidermonkey)
function ToObject(x) {
if (IS_STRING(x)) return new $String(x);
if (IS_SYMBOL(x)) return new $Symbol(x);
if (IS_NUMBER(x)) return new $Number(x);
if (IS_BOOLEAN(x)) return new $Boolean(x);
if (IS_NULL_OR_UNDEFINED(x) && !IS_UNDETECTABLE(x)) {
throw %MakeTypeError('null_to_object', []);
}
return x;
}
Jackpot. We can see the entire flow here. If it's a string, number, boolean, etc return a wrapper ($String and $Boolean and the likes represent the actual String or Number; see here); if it's an invalid argument, throw an error; otherwise, return the argument.
The spidermonkey source for that one goes deeper down the rabbit hole. It defines ToObject as such:
JS_ALWAYS_INLINE JSObject *
ToObject(JSContext *cx, HandleValue vp)
{
if (vp.isObject())
return &vp.toObject();
return ToObjectSlow(cx, vp, false);
}
So if it's not an Object, call ToObjectSlow. Buckle up Alice, there'll be C++. We need to take a look at what ToObejctSlow does:
JSObject *
js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack)
{
if (val.isNullOrUndefined()) {
...error throwing magic here...
return NULL;
}
return PrimitiveToObject(cx, val);
}
More indirection after looking whether the argument was null or undefined. The finale is here:
JSObject *
PrimitiveToObject(JSContext *cx, const Value &v)
{
if (v.isString()) {
Rooted<JSString*> str(cx, v.toString());
return StringObject::create(cx, str);
}
if (v.isNumber())
return NumberObject::create(cx, v.toNumber());
JS_ASSERT(v.isBoolean());
return BooleanObject::create(cx, v.toBoolean());
}
Pretty much the same as the v8 version, only with different taxonomy.
Now, as I said before, I think your question has more to do with the medium of representing the object you see. Firebug and chrome's devtools are more than apt at displaying an object. However, if you try to alert it, you'll see the unfortunate [object Object], because that's what ({}).toString() gives you (since it gives out a string of the form [object InternalClassName], again, as we've seen before).
As a bonus, try console.dir({foo : 'bar'})

To answer your first question
JavaScript has two main variable category types, primitives and Objects. You will often hear this, in JS everything is an Object. That is not entirely accurate. There are also primitive data types, which do nothing but hold values.
They have no methods and they are not instances of a wrapper type. So before you can call any method on them, they need to be converted to an object of the wrapper type. In JavaScript this conversion is automatic and it is called auto-boxing.
Allow me to demonstrate:
var firstString = "Test";
typeof firstString == "string"; // true
var secondString = new String("Test");
typeof secondString == "string"; // false
secondString.prototype.toString.call// [object String];
Notice what happens. There are actually two types above. One is string and the other one is [object String]. This tells you two things: secondString instanceof String is true. That is a wrapper type. Inside the core language you are seeing that String inherits from Object.
But the first string is just a memory reference, nothing more. When you call methods like firstString.replace(), firstString is automatically converted to its wrapper type. This is autoboxing.
The above behaviour stands for the following pairs:
Number autoboxing
var x = 5; var y = new Number(5);,
Boolean autoboxing
var x = false; var y = new Boolean(false);
RegExp autoboxing
var x = new RegExp("etc"); var y = /etc/;
Object.prototype.valueOf
The valueOf method is defined for any Object. In order for it to be called, it will convert all primitive types to their wrapper types and will leave existing objects unchanged. Now it will simply return the value held in the Object reference. So it's pretty simple and it is a way to FORCE AUTOBOXING. You are forcing the conversions I was mentioning earlier.
To answer your second question
Displaying the unfiltered result is simple. Use console.dir().
Look here.
({}).valueOf.call(myvar);
It is the exact equivalent of Object.prototype.valueOf.call(myVar);. Now you already know what valueOf does.
Assuming you know the way Function.prototype.call works, your statement will call the valueOf method in the scope of the object you pass as a this argument to Function.prototype.call(the first parameter is the this object reference).
var myvar = {
"name": "name"
};
({}).valueOf.call(myVar);
// is equivalent to
myVar.valueOf();

Related

Does it an error that professional javascript for web developers tell us how object convert to number?

Look at below code, what should it output ?
let obj = Object.create(null);
obj.valueOf = function () { console.log('hit valueOf!'); return "TO BE NaN";};
obj.toString = function () { console.log('hit toString!'); return "1";};
Number(obj)
// ?
According to professinal javascript, valueOf function returns "TO BE NaN" , and it will be convert to NaN, then toString function called.
Number Conversions
When applied to objects, the valueOf() method is called and the returned value is converted
based on the previously described rules. If that conversion results in NaN, the toString()
method is called and the rules for converting strings are applied.
As a matter of fact, it shows things here, function toString not called !
let obj = Object.create(null);
obj.valueOf = function () { console.log('hit valueOf!'); return "TO BE NaN";};
obj.toString = function () { console.log('hit toString!'); return "1";};
Number(obj)
// hit valueOf!
// NaN
Does it an error that professional javascript for web developers tell us how object convert to number?
Yes, that's a mistake in your book (and not the only one).
It should read more like1
When applied to objects, the valueOf() method is called and the returned value is converted based on the previously described rules if it is a primitive value. If that conversion results in NaN it returns an object, the toString() method is called and the rules for converting strings are applied.
Demonstrated:
const obj = Object.create(null);
obj.valueOf = function () { console.log('hit valueOf!'); return {};};
obj.toString = function () { console.log('hit toString!'); return "1";};
console.log(Number(obj))
1: Although still not quite correct. It should mention that the two methods are called only if they exist, and that an error is thrown if they both return an object.
Number(value), according to the specification, calls ToNumeric(value), which returns ToPrimitive(value, number). This second argument is the preferredType, resulting in:
If hint is string, then
Let methodNames be « "toString", "valueOf" ».
Else,
Let methodNames be « "valueOf", "toString" ».
For each element name of methodNames, do
a. Let method be ? Get(O, name).
b. If IsCallable(method) is true, then
Let result be ? Call(method, O).
If Type(result) is not Object, return result.
So in the case of Number(obj), the valueOf method really is supposed to be invoked first.
In other words, whatever the following sentence came from is incorrect:
When applied to objects, the valueOf() method is called and the returned value is converted based on the previously described rules. If that conversion results in NaN, the toString() method is called and the rules for converting strings are applied.

Some questions about JavaScript primitive data type

JavaScript has 7 primitive data types: string, number, boolean, null, undefined, symbol, bigint
I understand the three data types, string, number, boolean; I also understand bigint is introduced since number is not enough. One example why number is simple floating number:
var a = 1.000000000000001;
var b = 1.0000000000000001;
console.log(Number.isInteger(a)) //false
console.log(Number.isInteger(b)) // true
below are my questions:
What is undefined? Is it the same as void in c++? but void is a keyword. why undefined is a primitive data type in Javascript?
why null a data type? seems typeof null return is an object. what the difference between undefined and null? what the similar thing in c++?
what is symbol? I read this link, What is the motivation for bringing Symbols to ES6?, can someone give me an example in which we really need a symbol?
Basically, in javascript we have two categories of types: Primitives and Objects(functions and array are also objects.).
Primitives are copied by their values, objects are copied by their reference.
Undefined and void in c++ are not same. Javascript also has void operator, void operator in javascript and c++ is same, but comparing javascript properties to C++ is not something reasonable. C++ vs Java is more reasonable.
differences between null and undefined
1-
let name;
this is undefined, we declared but did not return any value. A function returns undefined if a value is not returned
We can also explicitly set this to undefined.
let name=null,
We use null in situations when we wanna clear the value of the variable.
2-we can use null in JSON but we cannot use undefined in JSON.
3-undefined is a type, while null is an empty object. Why null is an object, something needs to be solved by ECMA. It is a bug in javascript. Even the creator of javascript accepted that it was a mistake.
We use SYMBOLS to make properties or methods of an object private. So we hide the details and show only the essentials. It is called abstraction in javascript.
How to implement this:Let's create a simple class with "radius" property
class Circle {
constructor(radius) {
this.radius = radius;
}
}
A symbol is essentially a unique identifier. Every time we call this function we get a unique identifier. It is not a constructor function,though.
Symbol()===Symbol() //will be false
Implementation:
const _radius=Symbol()
class Circle {
constructor(radius) {
this[_radius] = radius; //since property name starts with _, we use bracket notation
}
}
now test this. Create an instance of Circle:
const c=new Circle;
console.log(Object.getOwnPropertyNames(c))// you will see a number on the console.

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.

Set a number return value for an object

I've previously asked a fairly similar question, and with this answer, I know it's possible to make objects that return strings when placed inside other strings. This would be some code based on that answer which would allow the object to have a custom string return value:
function myString(value) {
this.String = value;
}
myString.prototype.toString = function() {
return this.String;
}
var strObj = new myString('Foo');
//>> strObj
//<< myString {String: "Foo", toString: function}
//>> strObj+''
//<< "Foo"
//What I want in addition to this:
//>> +strObj
//<< 42
I originally got the idea for that original question by seeing the effects of Date objects within strings. So, since there's another feature of Date objects that could be quite useful, I'd like to know if there's a way for me to do the same thing as Date objects do when used in an expression (being converted to a number):
+new Date(); //1401414068943
I'd like my myString object to do the same. I've tried to continue the mindset of the prototype toString in the object, but although there is a JS method to convert to strings, there's no method - only a native function - to convert non-numbers to strings.
So is it possible for me to do this 'automatic object-to-number conversion' for my own objects, or is this some kind of sorcery only available to Date because it's native to JS?
I'd like the syntax to then be
var strObj = new myString('Foo', 42);
if that's possible.
I believe the prototype method you are looking for that handles object to numeric conversion is Object.prototype.valueOf()
It can be altered or customized just as you are altering toString()
Be aware that this sort of thing can be considered bad style when it may confuse other programmers (including yourself at a future date) as standard conversions can be redefined to behave differently than expected.
toString is the method that is invoked when an object is used in a string context (more exactly, when ToString is called on it). And yes, there is a similar method that is invoked when an objet is used in a numeric context (ToNumber): valueOf. If either doesn't exist, the other is used, for details see the DefaultValue algorithm.
function MyHybrid(str, num) {
this.string = str;
this.value = num;
}
MyHybrid.prototype.toString = function() {
return this.string;
};
MyHybrid.prototype.valueOf = function() {
return this.value;
};
var hybrid = new MyHybrid('Foo', 42)
String(hybrid) // "Foo"
Number(hybrid) // 42
However, it must be noted that strObj+'', which you have used for a conversion into a string, does not call ToString. The + operator can both act on numbers and strings, and therefore does only call ToPrimitive without a type hint - in which case valueOf is preferred (unless it is a Date object). hybrid+'' is equivalent to 42+'' and will yield "42".

Safe way to get a string representation of any JavaScript value or object

I want to get a string represention of any object or value in JavaScript. I did a couple of experiments.
> var a = document.createTextNode('foo'); a
"foo"
> var a = document.createTextNode('foo'); a.toString()
"[object Text]"
> var a = 1; a.toString()
"1"
> (1).toString()
"1"
> 1.toString()
SyntaxError: Unexpected token ILLEGAL
I have the following questions:
Why does 1.toString() fail?
Will the following function return me a string representation of every possible JavaScript object, value or literal? Function: function str(a) {return a.toString()}
Is there any other alternative to the function str I have written in the previous point?
1). Why does 1.toString() fail?
The JavaScript parser only uses a 1 character lookahead and can't determine if that's 1.0 or 1.toString(). You can use 1..toString() to get around that.
2). Will the following function return me a string representation of every possible JavaScript object, value or literal? Function: function str(a) {return a.toString()}
Any literal will be converted to a temporary object in order to have its toString() called. If the object has its own toString() defined, it will be called. Otherwise, it will use Object.prototype.toString() (having gone up the prototype chain) for almost all cases (the other case is an object with a null prototype).
3). Is there any other alternative to the function str I have written in the previous point?
Yes. You can invoke the toString() implicitly by concatenating an empty string, e.g. 1 + "". You can also use the String constructor, e.g. String(value) (thanks T.J. Crowder). The advantages of these other ones is no exception will be thrown if you attempt to call toString() on null or undefined.
However, these tricks will convert null and undefined to their string equivalents (almost never what you want). One dirty trick is to put the value in a literal array, e.g. [value] and then call toString() on it. This will actually invoke join(","), but seeing as it only has one member, the comma will never become part of the string.
The real power of doing this is that null and undefined will just become an empty string. If that's OK for your program, then it can be useful. Keep in mind to comment this solution as it's not immediately obvious what this code is doing. Alternatively, check value == null which will detect null and undefined and handle it appropriately.
However, if you're wanting a string in order to classify a value, you can get the type's [[Class]] like so...
var getInternalClass = function(value) {
return Object.prototype.toString.call(value).slice(8, -1);
};
This will invoke the Object's toString() and set the ThisBinding to the value provided as the argument. This is the only way to expose an object's internal [[Class]]. The advantage of this (over typeof, for example) is that primitives and objects will always return the same value (with the primitives being converted to temporary objects, boxed by the call() context in non-strict mode).
for 1.toString(), you need to do:
1 .toString() //add space before dot (.) to avoid taking it as decimal
shortest way (alternative to function str ) to convert to string is:
var str = str + '';
Your str(a) function is correct but it will call the default implementation of toString() inherited from Object. So yes, your function will give you string representation of every JS object but not in way you want it. You need to override it.
var o = new Object();
o.toString(); // returns [object Object]
See here for reference and overriding: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString

Categories