I'm struggling to understand this JavaScript behaviour (javascript n00b alert!!!). Source of the question is a typo in my code which led to the undesired situation. What I wanted to know is the length of a certain number. For example, if its 100, then answer should be 3, if 5893 then answer should be 4 and so on. To achieve this what I did simply is convert number to a string and then invoke .length on the string.
private getNumberLength(num: number) {
return num.toString().length;
}
In the above return statement, I had typo such that it looked like
return num.toString.length;
The result was no compilation error and getNumberLength always returned 1. I fail to understand this (why 1?). Can somebody please help me understand this?
Below you can quickly test if you wish
var num = 666;
console.log('Length on toString(): ' + num.toString().length);
console.log('Length on toString: ' + num.toString.length);
Looks like this is Javascript behavior of automatically boxing primitive values to its Object counterpart to call its method.
The function looks correct, but to answer your question (I don't understand the downvotes).
toString is a reference to the function. The reason why toString.length works is because as quoted from Mozilla.
Function.length = The length property indicates the number of parameters expected by the function.
So if you redeclare your function as
private getNumberLength(num: number, num2: number) {
return num.toString().length;
}
getNumberLength.length will return 2.
toString.length will return 1 as it only expects one parameter.
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length
toString() is invoking the function toString (and getting its return value). This is performing the actual conversion to String.
All the best with Javascript
Related
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.
I know that there probably wouldn't be any reason to use this over some other way of creating a variadic function, but is it semantically possible to define something to do this? For example, it could be called like sum(1)(2)(3)();. I attempted to implement it with the following code:
function sum(x) {
return function(y) {
if (!y) return 0;
else return x + sum(y);
};
}
However, when called this gives TypeError: string is not a function. What would cause this to return a string? When I inspected it more closely, it looked like it was returning the code for the function as a string, which makes no sense to me. That said, I am not extensively familiar with the semantics of intricate JavaScript, so I could be missing some huge concept in it.
As mentioned in the comments, x is a number and sum(y) is a function. Using the + operator on them uses their gcd type which are strings, and cannot be called.
You cannot curry operators in javascript. You need to return a curried function explicitly, and tell it when to calculate the values. An appropriate fix for our function would be
function sum(x) {
return function(y) {
if (!y) return x;
// ^
else return sum(x + y);
// ^^^
};
}
sum(1) -> ref Function(y)
sum(1)(2) -> invocation Function(2)
Function(2) -> 1 + (sum(y) -> sum(2) -> ref Function(y))
Now, 1 + (ref Function(y)), as you can see, you're adding a number and a reference.
The javascript is thinking that you're trying to concatenate, hence it calls ToString method of Function, internally, which gives the function body as a string.
So, the next time you call, you're calling on a String and hence the error as String isn't a function.
A correct code would be what Bergi has implemented.
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
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();
I found three ways to cast a variable to String in JavaScript.
I searched for those three options in the jQuery source code, and they are all in use.
I would like to know if there are any differences between them:
value.toString()
String(value)
value + ""
DEMO
They all produce the same output, but does one of them better than the others?
I would say the + "" has an advantage that it saves some characters, but that's not that big advantage, anything else?
They do behave differently when the value is null.
null.toString() throws an error - Cannot call method 'toString' of null
String(null) returns - "null"
null + "" also returns - "null"
Very similar behaviour happens if value is undefined (see jbabey's answer).
Other than that, there is a negligible performance difference, which, unless you're using them in huge loops, isn't worth worrying about.
There are differences, but they are probably not relevant to your question. For example, the toString prototype does not exist on undefined variables, but you can cast undefined to a string using the other two methods:
var foo;
var myString1 = String(foo); // "undefined" as a string
var myString2 = foo + ''; // "undefined" as a string
var myString3 = foo.toString(); // throws an exception
http://jsfiddle.net/f8YwA/
They behave the same but toString also provides a way to convert a number binary, octal, or hexadecimal strings:
Example:
var a = (50274).toString(16) // "c462"
var b = (76).toString(8) // "114"
var c = (7623).toString(36) // "5vr"
var d = (100).toString(2) // "1100100"
In addition to all the above, one should note that, for a defined value v:
String(v) calls v.toString()
'' + v calls v.valueOf() prior to any other type cast
So we could do something like:
var mixin = {
valueOf: function () { return false },
toString: function () { return 'true' }
};
mixin === false; // false
mixin == false; // true
'' + mixin; // "false"
String(mixin) // "true"
Tested in FF 34.0 and Node 0.10
According to this JSPerf test, they differ in speed. But unless you're going to use them in huge amounts, any of them should perform fine.
For completeness: As asawyer already mentioned, you can also use the .toString() method.
if you are ok with null, undefined, NaN, 0, and false all casting to '' then (s ? s+'' : '') is faster.
see http://jsperf.com/cast-to-string/8
note - there are significant differences across browsers at this time.
Real world example: I've got a log function that can be called with an arbitrary number of parameters: log("foo is {} and bar is {}", param1, param2). If a DEBUG flag is set to true, the brackets get replaced by the given parameters and the string is passed to console.log(msg). Parameters can and will be Strings, Numbers and whatever may be returned by JSON / AJAX calls, maybe even null.
arguments[i].toString() is not an option, because of possible null values (see Connell Watkins answer)
JSLint will complain about arguments[i] + "". This may or may not influence a decision on what to use. Some folks strictly adhere to JSLint.
In some browsers, concatenating empty strings is a little faster than using string function or string constructor (see JSPerf test in Sammys S. answer). In Opera 12 and Firefox 19, concatenating empty strings is rediculously faster (95% in Firefox 19) - or at least JSPerf says so.
On this page you can test the performance of each method yourself :)
http://jsperf.com/cast-to-string/2
here, on all machines and browsers, ' "" + str ' is the fastest one, (String)str is the slowest