With reference to below code written in Javascript.
let a = {
value: 2,
toString: function() {
return ++this.value;
}
}
if (a == 3 && a == 4) {
console.log('Condition is true');
}
The output is "Condition is true". Looks like it invokes toString() function. But how?
When I replace "==" with "===", condition does not evaluates to true and it does not invoke toString() function this time?
Can someone explain me in detail what's going under the hood?
When you do == it is not a strict comparison so what it does for the condition a == 3 && a == 4, is that first it compares a == 3. Since, it is not a strict comparison, it will change a to string. And since you have toString() in a, that will increment the value of a from 2 to 3 and hence a == 3 result in true. Then, a == 4 checks the same way and this time the value of a is 3 so when it checks for a == 4 it results in true by invoking the toString() function of a.
let a = {
value: 2,
toString: function() {
return ++this.value;
}
}
if (a == 3 && a == 4) {
console.log('Condition is true');
}
However, when you use ===, it works as a strict comparison and the type of LHS should match RHS. Thus, a is a object in LHS and there is a number type in RHS, so it results false for a == 3 and hence, a == 3 && a == 4
let a = {
value: 2,
toString: function() {
return ++this.value;
}
}
if (a === 3 && a === 4) {
console.log('Condition is true');
} else {
console.log('Condition is false');
}
The output is "Condition is true". Looks like it invokes 'toString()'
function.
Everytime you use == operator between two variables with different types it is invoked internally toString method which will coerce one member. Have a look at type coercion
But how?
You're creating a custom toString function for your a object that changes what it returns each time it is used such that it satisfies all two conditions.
You can also use valueOf method.
How about === operator ?
Otherwise, the === operator will not do the conversion.
What means this ?
If you're using === operator with two values with different type === will simply return false.
Happen to notice that toString is invoked in interpolated string, executed prioritized than valueOf
var a = {
value: 1000,
toString: () => 'call toString method',
valueOf: () => 'call valueOf method'
};
console.log(`interpreted value: ${a}`); // interpreted value: call toString method
In addition to Mihai's answer, === is a strict typechecking equality operator which checks for the type of the operands and the values as well.
In your case, the type of a is an object, whereas 3 and 4 are numbers. So the condition doesn't evaluate to true.
it checks whether object has falsy value or not when you use == thats why you get true from a == 4 and a == 3. Have a look at type coercion. It does not coerce variables when comparing them and thats why you cannot get into the block statement
You can find detail information of how '==' and '===' works in javascript from link below:
Equality comparisons and sameness
In this URL refer 'Loose equality using ==' section.
In you case your comparison as a == 3. a is object and 3 is number. So comparison will take place as ToPrimitive(a) == 3. What ToPrimitive(a) do is it attempting to invoke varying sequences of a.toString and a.valueOf methods on A. This is how your toString function is called.
On the surface your question looks like the sheer difference between == and === operators, but in fact there is bit more to it.
For your first question, since javascript is not strictly typed language, there is 2 operators, == will try to convert the left operand to right ones type if possible whereas === will give false if the types are different with the exception of NaN.
A more interesting question is when toString method gets called. Normally when you create an object, either by typing an object literal or through a constructor, it will inherit toString from the Object, you easily check this:
var u = function(){};
var w = {};
u.prototype.toString === Object.prototype.toString //true
w.toString === Object.prototype.toString //true as well
Now what you might forget is that there is also a valueOf method:
u.prototype.valueOf === Object.prototype.valueOf //true
w.valueOf === Object.prototype.valueOf //true as well
But what does it do? I points to itself:
w.valueOf === w //true
u.prototype.valueOf() === u.prototype //true as well
So when an object is given, the first choice is to use the toString becuae valueOf will result in the object itself. What does toString give by default? it can depend on what is there on its prototype chain:
w.toString() //"[object Object]"
u.toString() //"function (){}"
u.prototype.toString.call(u) //"[object Function]"
So by default the first choice is to use the nearest toString method of the object. Now to see what happens if we override BOTH valueOf and toString, let me construct this object:
var x = {
inner:10,
ledger:[],
result:[],
timeout:0,
toString:function(){
console.log("String");
clearTimeout(this.timeout);
this.timeout = setTimeout((function(){
this.result = this.ledger;
this.ledger = []}).bind(this)
,0) ;
this.ledger.push("toString");
this.ledger.slice(-2);
return String(this.inner);
},
valueOf:function(){
console.log("Valueof");
clearTimeout(this.timeout);
this.timeout = setTimeout((function(){
this.result = this.ledger;
this.ledger = []}).bind(this)
,0) ;
this.ledger.push("valueOf");
this.ledger.slice(-2);
return this.inner;
}
}
This object will keep a "ledger", an array of valueOf or toString or both called during type conversion. It will also console.log. So,here are some operations that will trigger type conversion just like ==:
+x //10
//ValueOf
"5" + x //"510"
//ValueOf
x + [] //10
//ValueOf
x + "str"//"10str"
//ValueOf
x.toString() //"10"
//String
String(x) //"10"
//String
x + {u:""} //"10[object Object]"
//valueOf
So in many cases if a non-default valueOf is found that is used. If the right operand is a string, then the returned value from valueOf is converted to string rather than toString on the object. To override default behavior you can force call to string by using toString or String( as shown in the examples. So the priority list goes as:
Custom valueOf >> custom toString >> default toString >>>> default valueOf
Related
I know == operator performs the type coercion. But I can't understand the below behaviour.
const x = new Boolean(false);
if (x) {
console.log("if(x) is true");
}
if (x == false) {
console.log("if(x == false) is true");
}
Surprisingly, above snippet prints both lines:
if(x) is true
if(x == false) is true
Can someone explain this weird behaviour or there is something fundamental I'm missing?
As mentioned by other answers, that's because x is an Object – a Boolean object, but still an object, since you're using the new operator – and is coerced only when you compare x to false: the if (x) is checking if x is a truthy value, and therefore doesn't need coercion (there are no other operands involved): an object is always "true" (weeeell… almost always: typeof null returns object but it's a falsy value. But that's another story…).
You can easily checking when the coercion is invoked tapping the toPrimitive symbol:
var x = new Boolean(false);
// first of all, if it wasn't an object you couldn't
// do this
x[Symbol.toPrimitive] = function(hint) {
console.log({hint});
return this.valueOf();
}
// no console.log yet
if (x) {
console.log("if(x) is true");
}
// here you got the console.log before the one
// inside the block, since the coercion happens
// in the `if`
if (x == false) {
console.log("if(x == false) is true");
}
new Boolean(false) produces an object. Objects are always truthy even if they wrap a falsy primitive value. For example, new String("") is also truthy despite "" being falsy.
On the other hand, when you do new Boolean(false) == false, it coerces the object to its primitive value for the comparison. Incidentally, new Boolean(false) === false is not true, since their types don't match.
As a general rule, you shouldn't use object constructors for primitive types unless you have a specific reason for it, to avoid unexpected behavior like this.
when you do if (expression) in javascript the expression is evaluated by being cast to a boolean.
Somewhat confusingly, Boolean(new Boolean(false)) evaluates to true because as Nick said it is still an object. This is what is causing the behaviour you're confused about.
Good read for more info https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/
If you write
const x = new Boolean(false);
typeof x will return object. The type object is "truthy", which means it evaluates to true if there's no operator like ==. However, the value of it is false, which is why the second statement evaluates to true as well.
So the if statements behave differently, because the if without operator checks whether the type is truthy or falsy (in this case truthy -> true) and the if with the comparison (==) calls .valueOf() which is false.
You shouldn't be using new wrappers for this scenario anyway.
const x = false;
is enough. For casting, you can use Boolean() without new wrapper.
To check whether a value is truthy, you can use a double negation:
const x = new Boolean(false);
if (x) console.log(!!x);
if (x == false) console.log(x.valueOf());
You should be using Boolean(false) instead of new Boolean(false), since Boolean is a function.
Otherwise you get an empty object {}, which is not the same type as what is returned by the function itself, which is a boolean.
const x = new Boolean(false);
const y = Boolean(false);
console.log(x, typeof x);
console.log(y, typeof y);
In the your first test, you only check if the value is truthy, and an empty object is truthy, since x = {}, the test passes:
const x = new Boolean(false);
console.log(x, !!x, !!{}, Boolean(x))
if (x) {
console.log("if(x) is true");
}
However, when using ==, the operator coerces new Boolean(false) to its primitive value using x.valueOf which is false and thus the equality passes.
const x = new Boolean(false);
console.log(x.valueOf())
Just curious:
4 instanceof Number => false
new Number(4) instanceof Number => true?
Why is this? Same with strings:
'some string' instanceof String returns false
new String('some string') instanceof String => true
String('some string') instanceof String also returns false
('some string').toString instanceof String also returns false
For object, array or function types the instanceof operator works as expected. I just don't know how to understand this.
[new insights]
Object.prototype.is = function() {
var test = arguments.length ? [].slice.call(arguments) : null
,self = this.constructor;
return test ? !!(test.filter(function(a){return a === self}).length)
: (this.constructor.name ||
(String(self).match ( /^function\s*([^\s(]+)/im )
|| [0,'ANONYMOUS_CONSTRUCTOR']) [1] );
}
// usage
var Newclass = function(){}; // anonymous Constructor function
var Some = function Some(){}; // named Constructor function
(5).is(); //=> Number
'hello world'.is(); //=> String
(new Newclass()).is(); //=> ANONYMOUS_CONSTRUCTOR
(new Some()).is(); //=> Some
/[a-z]/.is(); //=> RegExp
'5'.is(Number); //=> false
'5'.is(String); //=> true
value instanceof Constructor is the same as Constructor.prototype.isPrototypeOf(value) and both check the [[Prototype]]-chain of value for occurences of a specific object.
Strings and numbers are primitive values, not objects and therefore don't have a [[Prototype]], so it'll only work if you wrap them in regular objects (called 'boxing' in Java).
Also, as you noticed, String(value) and new String(value) do different things: If you call the constructor functions of the built-in types without using the new operator, they try to convert ('cast') the argument to the specific type. If you use the new operator, they create a wrapper object.
new String(value) is roughly equivalent to Object(String(value)), which behaves the same way as new Object(String(value)).
Some more on types in JavaScript: ECMA-262 defines the following primitive types: Undefined, Null, Boolean, Number, and String. Additionally, there is the type Object for things which have properties.
For example, functions are of type Object (they just have a special property called [[Call]]), and null is a primitive value of type Null. This means that the result of the typeof operator doesn't really return the type of a value...
Aditionally, JavaScript objects have another property called [[Class]]. You can get it via Object.prototype.toString.call(value) (this will return '[objectClassname]'). Arrays and functions are of the type Object, but their classes are Array and Function.
The test for an object's class given above works when instanceof fails (e.g. when objects are passed between window/frame boundaries and don't share the same prototypes).
Also, you might want to check out this improved version of typeof:
function typeOf(value) {
var type = typeof value;
switch(type) {
case 'object':
return value === null ? 'null' : Object.prototype.toString.call(value).
match(/^\[object (.*)\]$/)[1]
case 'function':
return 'Function';
default:
return type;
}
}
For primitives, it will return their type in lower case, for objects, it will return their class in title case.
Examples:
For primitives of type Number (eg 5), it will return 'number', for wrapper objects of class Number (eg new Number(5)), it will return 'Number';
For functions, it will return 'Function'.
If you don't want to discern between primitive values and wrapper objects (for whatever, probably bad reason), use typeOf(...).toLowerCase().
Known bugs are some built-in functions in IE, which are considered 'Object' and a return value of 'unknown' when used with some COM+ objects.
You may try to evaluate:
>>> typeof("a")
"string"
>>> typeof(new String("a"))
"object"
>>> typeof(4)
"number"
>>> typeof(new Number(4))
"object"
As stated in Christoph's answer, string and number literals are not the same as String and Number objects. If you use any of the String or Number methods on the literal, say
'a string literal'.length
The literal is temporarily converted to an object, the method is invoked and the object is discarded.
Literals have some distinct advantages over objects.
//false, two different objects with the same value
alert( new String('string') == new String('string') );
//true, identical literals
alert( 'string' == 'string' );
Always use literals to avoid unexpected behaviour!
You can use Number() and String() to typecast if you need to:
//true
alert( Number('5') === 5 )
//false
alert( '5' === 5 )
In the case of primitive numbers, the isNaN method could also help you.
This is a nuance of Javascript which I've found catches some out. The instanceof of operator will always result in false if the LHS is not an object type.
Note that new String('Hello World') does not result in a string type but is an object. The new operator always results in an object. I see this sort of thing:
function fnX(value)
{
if (typeof value == 'string')
{
//Do stuff
}
}
fnX(new String('Hello World'));
The expectation is that "Do Stuff" will happen but it doesn't because the typeof the value is object.
Just tried an experiment,
var f = function() { alert("yay, a function!"); };
console.log(f == function() { alert("yay, a function!"); });
, which printed false to the console. But, if Javascript functions are to be thought of as objects, then wouldn't that be no different than
var x = 5;
console.log(x == 5);
???
in javascript, reference types can't be compared using equality operator(==).
so consoloe.log([1,2]==[1,2]) will return false.
and because function are instance of Object so it will also return false.
You can NOT use equality operator except for primitive types such as strings and numbers
Warning: creating extensions to native object and/or properties is considered bad form, and is bound to cause problems. Do not use this if it is for code that you are not using solely for you, or if you don't know how to use it properly
I know you can use Object, String, Number, Boolean, etc. to define a method, something like this:
String.prototype.myFunction = function(){return this;} //only works on strings.
But what I need to be able to do is use that on any value, and access the value in the function.
I googled, and looked here, but couldn't find anything suitable.
2/18/15 Edit: Is there any workaround to having this be a property of any Object if I use Object.prototype?
Per Request, here is the current function that is used for isString()
function isString(ins) {
return typeof ins === "string";
}
Following on a few answers, I have come up with some code and errors caused by it.
Object.prototype.isString = function() {
return typeof this === "string";
}
"5".isString() //returns false
"".isString() //returns false
var str = "string"
str.isString() //returns false
A “dot operator function” is called a method. The cleanest way to create a method in JavaScript that can work on any data type is to create a wrapper. For example:
var Wrapper = defclass({
constructor: function (value) {
this.value = value;
},
isString: function () {
return typeof this.value === "string";
},
describe: function () {
if (this.isString()) {
alert('"' + this.value + '" is a string.');
} else {
alert(this.value + " is not a string.");
}
}
});
var n = new Wrapper(Math.PI);
var s = new Wrapper("Hello World!");
n.describe(); // 3.141592653589793 is not a string.
s.describe(); // "Hello World!" is a string.
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
By creating your own wrapper constructor you ensure that:
Your code doesn't mess with other peoples' code.
Other people's code doesn't mess with your code.
You keep the global scope and native prototypes clean.
Several popular JavaScript libraries like underscore and lodash create wrapper constructors for this very purpose.
First of all, why defining properties on Object (or other builtin types) is frowned upon - they show up in unexpected places. Here is some code that outputs the total number of feet some characters have:
var feetMap = {
jerry : 4,
donald : 2,
humpty: 0
}
function countFeet(feetMap) {
var allFeet = 0;
for (var character in feetMap) {
allFeet += feetMap[character];
}
return allFeet;
}
console.log('BEFORE DEFINITION', countFeet(feetMap));
Object.prototype.isString = function() {
return typeof this === "string";
};
console.log('AFTER DEFINITION', countFeet(feetMap));
Note how simply defining your isString function will influence the result of the countFeet function which now iterates over one unexpected property. Of course, this can be avoided if the iteration was protected with hasOwnProperty check, or if the property was defined as non-enumerable.
Another reason to avoid defining properties on builtin types it the possibility of collision. If everyone defined their own isNumber method that gave slightly different results depending on use cases - one could consider the string "42" a number and another could say it's not - subtile bugs would crop all over the place when people used multiple libraries.
The question is - why do you need a method that can affect any value type? A method should be something that is inherent to the class of objects it belongs to. Having an isString method makes no sense on a Number object - it simply doesn't have any relevance to Numbers.
What makes more sense is to have a function/method that can return the type of the value given to it as parameter:
var Util = Util || {};
Util.isString(value) {
return typeof value === "string";
}
Util.isString('test') // true
Util.isString(5) // false
The reason why your current code
Object.prototype.isString = function() {
return typeof this === "string";
}
"5".isString() //returns false
"".isString() //returns false
var str = "string"
str.isString() //returns false
isn't working is because when you access a property on a primitive value, JS creates a wrapper object of the apropriate types and resolves the property on that wrapper object (after which it throws it away). Here is an example that should elucidate it:
Object.prototype.getWrapper = function(){
return this;
}
console.log((5).getWrapper()); // Number [[PrimitiveValue]]:5
console.log("42".getWrapper()); // String [[PrimitiveValue]]:"42"
Note that the primitive value 5 and the object new Number(5) are different concepts.
You could alter your function to mostly work by returning the type of the primitive value. Also, don't forget to make it non-enumerable so it doesn't show up when you iterate over random Objects.
Object.defineProperty(Object.prototype, 'isString', {
value : function() {
return typeof this.valueOf() === "string";
},
enumerable : false
});
"5".isString() //returns true
"".isString() //returns true
var str = "string"
str.isString() //returns true
Object.prototype.isString = function() {
return typeof this === "string";
}
"5".isString() //returns false
"".isString() //returns false
var str = "string"
str.isString() //returns false
If anyone could explain a workaround for the function being a property of any object, and why the current method isn't working, I will provide 125 rep.
Answer:
Well in javascript, when you are calling a sub method/property of a object, like "myFunction" (object.myFunction or object["MyFunction"])
it will start to see if the object itself have it.
IF NOT: it will follow the prototype chain(like superclass in normal oop),
until it finds a "parent/superclass" with the method/property.
The last step in this prototype chain is Object.
If Object dosnt have the method, it will return "undefined".
When you extending the Object class itself it will alway look at
any object calling the method as a Object (In oop: All classes are also Object in addition the is own classtype)
This is like missing a "cast" in normal OOP.
So the reason why your function returns false is that its a "object" not a "string" in this context
Try making this function:
Object.prototype.typeString = function() {
return typeof this;
}
"5".typeString() //returns "object"
As everbody says it really bad idea to extend any of the native JS classes, but a workaround would start with something like this:
Object.prototype.objectTypeString = function(){
return Object.prototype.toString.call(this);
}
Here is a fiddle:
http://jsfiddle.net/fwLpty10/13/
Note that null dosnt have prototype and NaN (NotANumber) is condidered a Number!!!
This means that you will always need to check is a variable is null,
before calling this method!
Object.prototype.isString = function(){
return Object.prototype.toString.call(this) === "[object String]";
};
Final fiddle: http://jsfiddle.net/xwshjk4x/5/
The trick here is that this methods returns the result of the toString method, that are called with "this", which means that in the context of the toString method, the object you call it on, are its own class (not just any supertype in the prototype chain )
The code posted that extends the Object prototype will work, if corrected.
However, it makes an incorrect assumption about what this is inside the invoked method. With the posted code the following output is correct and to be expected (barring a few old implementation bugs);
"5".isString() //returns false
This is because JavaScript will "wrap" or "promote" the primitive value to the corresponding object type before it invokes the method - this is really a String object, not a string value. (JavaScript effectively fakes calling methods upon primitive values.)
Replace the function with:
Object.prototype.isString = function() {
return this instanceof String;
}
Then:
"5".isString() // => true (a string was "promoted" to a String)
(5).isString() // => false (`this` is a Number)
Another solution to this is also to use polymorphism; with the same "pitfalls"1 of modifying standard prototypes.
Object.prototype.isString = function () { return false; }
String.prototype.isString = function () { return true; }
1The concern of adding a new enumerable property to the global protoypes can be mitigated with using defineProperty which creates a "not enumerable" property by default.
Simply change
x.prototype.y = ..
to
Object.defineProperty(x.prototype, 'y', { value: .. })
(I am not defending the use of modifying the prototype; just explaining the original problematic output and pointing out a way to prevent the enumeration behavior.)
To show u some example:
String.prototype.myFunction = function() {
return this+"asd";
};
this function will add "asd" to each string when myFunction() is called.
var s = "123";
s = s.myFunction();
//s is now "123asd"
Before we start, few important statements to remember and be aware of (true for all string literal/primitive, String object, number literal/primitive, Number object etc.):
All objects in JavaScript are descended from Object and inherit methods and properties from Object.prototype – String, Number etc (much like Java).
JS has 6 primitive types – string, number, boolean, null, undefined and symbol
JS has their corresponding wrapper objects – String, Number, Boolean and Symbol
As you can see above, JS has string as a primitive as well an Object
Primitive is not of type Object.
String literal is a primitive and String object is of type Object.
The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor. (first condition to get TRUE here is that instanceof should be used against an Object or its subclass)
The typeof operator returns a string indicating the type of the unevaluated operand.
String as primitive:
String primitive or literal can be constructed in following ways:
var str1 = “Hello”;
var str2 = String(“Hello”);
typeof str1; //Output = "string"
typeof str2; //Output = "string"
str1 instanceof (String || Object) //Output = false because it is a primitive not object
str2 instanceof (String || Object) //Output = false because it is a primitive not object
String as Object:
String object can be constructed by calling its constructor from new object:
var str3 = new String(“Hello”);
typeof str3; //Output = "string"
str3 instanceof (String) //Output = true because it is a String object
str3 instanceof (Object) //Output = true because it is an Object
Above all may look little obvious but it was necessary to set the ground.
Now, let me talk about your case.
Object.prototype.isString = function() {
return typeof this === "string";
}
"5".isString() //returns false
"".isString() //returns false
var str = "string"
str.isString() //returns false
You are getting FALSE as o/p because of concept called as Auto-boxing. When you call any method on string literal then it gets converted to String object. Read this from MSDN - “Methods for String Literals”, to be sure in yourself.
So, in your prototype when you will check type using typeof then it will never be a literal (typeof == "string") because it is already converted into an object. That's the reason you were getting false, if you will check typeof for object then you will get true, which I am going to talk in detail below:
typeof will give information on what type of entity it is - an object or a primitive (string, number etc) or a function.
instanceof will give information on what type of JS Object it is - Object or String or Number or Boolean or Symbol
Now let me talk about solution which is provided to you. It says to do a instanceof check, which is 100% correct, but with a note that upon reaching your prototype it could be of object type or function type. So, the solution which I am providing below will give you a picture of the same.
My recommendation is to have a generic function which would return you the type of instance, and then you can do whatever you want based on if it is a Number or String etc. isString is good but then you have to write isNumber etc., so instead have a single function which will return you the type of instance and can even handle function type.
Below is my solution:
Object.prototype.getInstanceType = function() {
console.log(this.valueOf());
console.log(typeof this);
if(typeof this == "object"){
if(this instanceof String){
return "String";
} else if(this instanceof Boolean){
return "Boolean";
} else if(this instanceof Number){
return "Number";
} else if(this instanceof Symbol){
return "Symbol";
} else{
return "object or array"; //JSON type object (not Object) and array
}
} else if(typeof this == "function"){
return "Function";
} else{
//This should never happen, as per my knowledge, glad if folks would like to add...
return "Nothing at all";
}
}
Output:
new String("Hello").getInstanceType() //String
"Hello".getInstanceType() //String
(5).getInstanceType() //Number
(true).getInstanceType() //Boolean
Symbol().getInstanceType() //Symbol
var ddd = function(){}
var obj = {}
obj.getInstanceType() //object or array
var arr = []
arr.getInstanceType() //object or array
ddd.getInstanceType() //Function
($).getInstanceType() //Function, because without double quotes, $ will treated as a function
("$").getInstanceType() //String, since it came in double quotes, it became a String
To wrap up: Your 2 concerns as below
But what I need to be able to do is use that on any value, and access
the value in the function.
You can access the value in your function using this. In my solution you can see console.log(this.valueOf());
Is there any workaround to having this be a property of any Object if
I use Object.prototype?
You can achieve it from Object.prototype.getInstanceType as per above solution, and you can invoke it on any valid JS object and you will get the desired information.
Hope this helps!
From the MDN Description of Object:
All objects in JavaScript are descended from Object
So, you can add methods to Object.prototype, which can then be called on anything. For example:
Object.prototype.isString = function() {
return this.constructor.name === 'String';
}
console.log("hi".isString()); //logs true
console.log([].isString()); //logs false
console.log(5..isString()); //logs false
You could create this isX functions for each type of primitive there is, if you wanted. Either way, you can add methods to every type, since everything in JavaScript descends from an Object.
Hope that helps, and good luck :)
--edit--
I did want to point out that just because you can do this doesn't mean that you should. It's generally a bad practice to extend built-in functionality of JavaScript, even more so for a library that others will use. It depends on your use-case, though. Best of luck.
Discussions aside, that this is not good practice and not a common approach like the wrapper-constructor, you should achieve this with either asking for the constructor's name:
Object.defineProperty(Object.prototype, 'isString', {
value: function() { return this.constructor.name === "String"; }
});
or with the also already mentioned instanceof method:
Object.defineProperty(Object.prototype, 'isString', {
value: function() { return this instanceof String; }
});
Explanation why your method didn't work is taking care of in this post.
If you want your new defined property to be enumerable, configurable or writable, you should take a look at the docs for defineProperty.
As a few others have pointed out your code is almost correct expect for the typeof this === 'string' part which doesn't work due to JavaScript's quirky behavior when it comes to primitives vs. objects. One of the most robust ways to test if an object is a string is with Object.prototype.toString.call(this) === '[object String]' (check out this article). With that in mind you could simply write your implementation of isString like so:
Object.prototype.isString = function () {
return Object.prototype.toString.call(this) === '[object String]';
};
"abc".isString(); // ==> true
"".isString(); // ==> true
1..isString(); // ==> false
{}.isString(); // ==> false
This is because string literals are of native string type, not actually an instance of String object so, in fact, you cannot actually call any method from Object or String prototype.
What is happening is that when you try to call any method over a variable which type is string, Javascript is automatically coercing that value to a newly constructed String object.
So
"abc".isString();
Is the same as:
(new String("abc")).isString();
The side effect of that is that what you are receiving in your isString() method is an (variable of type) Object which, also, is an instance of the String object.
Maybe you could see it more clearly with a simplified example:
var a = "Hello";
console.log(typeof a); // string
var b = new String("Hello");
console.log(typeof b); // object
By the way, the best chance you have to detect string in your function, as many others said, is to check if it is an instance of the String object with:
foo instanceof String
If you want to also check over other possible types, you should do a double check like the following:
function someOtherFn(ins) {
var myType = typeOf ins;
if (myType == "object" && ins instanceof String) myType = "string";
// Do more stuf...
}
Just curious:
4 instanceof Number => false
new Number(4) instanceof Number => true?
Why is this? Same with strings:
'some string' instanceof String returns false
new String('some string') instanceof String => true
String('some string') instanceof String also returns false
('some string').toString instanceof String also returns false
For object, array or function types the instanceof operator works as expected. I just don't know how to understand this.
[new insights]
Object.prototype.is = function() {
var test = arguments.length ? [].slice.call(arguments) : null
,self = this.constructor;
return test ? !!(test.filter(function(a){return a === self}).length)
: (this.constructor.name ||
(String(self).match ( /^function\s*([^\s(]+)/im )
|| [0,'ANONYMOUS_CONSTRUCTOR']) [1] );
}
// usage
var Newclass = function(){}; // anonymous Constructor function
var Some = function Some(){}; // named Constructor function
(5).is(); //=> Number
'hello world'.is(); //=> String
(new Newclass()).is(); //=> ANONYMOUS_CONSTRUCTOR
(new Some()).is(); //=> Some
/[a-z]/.is(); //=> RegExp
'5'.is(Number); //=> false
'5'.is(String); //=> true
value instanceof Constructor is the same as Constructor.prototype.isPrototypeOf(value) and both check the [[Prototype]]-chain of value for occurences of a specific object.
Strings and numbers are primitive values, not objects and therefore don't have a [[Prototype]], so it'll only work if you wrap them in regular objects (called 'boxing' in Java).
Also, as you noticed, String(value) and new String(value) do different things: If you call the constructor functions of the built-in types without using the new operator, they try to convert ('cast') the argument to the specific type. If you use the new operator, they create a wrapper object.
new String(value) is roughly equivalent to Object(String(value)), which behaves the same way as new Object(String(value)).
Some more on types in JavaScript: ECMA-262 defines the following primitive types: Undefined, Null, Boolean, Number, and String. Additionally, there is the type Object for things which have properties.
For example, functions are of type Object (they just have a special property called [[Call]]), and null is a primitive value of type Null. This means that the result of the typeof operator doesn't really return the type of a value...
Aditionally, JavaScript objects have another property called [[Class]]. You can get it via Object.prototype.toString.call(value) (this will return '[objectClassname]'). Arrays and functions are of the type Object, but their classes are Array and Function.
The test for an object's class given above works when instanceof fails (e.g. when objects are passed between window/frame boundaries and don't share the same prototypes).
Also, you might want to check out this improved version of typeof:
function typeOf(value) {
var type = typeof value;
switch(type) {
case 'object':
return value === null ? 'null' : Object.prototype.toString.call(value).
match(/^\[object (.*)\]$/)[1]
case 'function':
return 'Function';
default:
return type;
}
}
For primitives, it will return their type in lower case, for objects, it will return their class in title case.
Examples:
For primitives of type Number (eg 5), it will return 'number', for wrapper objects of class Number (eg new Number(5)), it will return 'Number';
For functions, it will return 'Function'.
If you don't want to discern between primitive values and wrapper objects (for whatever, probably bad reason), use typeOf(...).toLowerCase().
Known bugs are some built-in functions in IE, which are considered 'Object' and a return value of 'unknown' when used with some COM+ objects.
You may try to evaluate:
>>> typeof("a")
"string"
>>> typeof(new String("a"))
"object"
>>> typeof(4)
"number"
>>> typeof(new Number(4))
"object"
As stated in Christoph's answer, string and number literals are not the same as String and Number objects. If you use any of the String or Number methods on the literal, say
'a string literal'.length
The literal is temporarily converted to an object, the method is invoked and the object is discarded.
Literals have some distinct advantages over objects.
//false, two different objects with the same value
alert( new String('string') == new String('string') );
//true, identical literals
alert( 'string' == 'string' );
Always use literals to avoid unexpected behaviour!
You can use Number() and String() to typecast if you need to:
//true
alert( Number('5') === 5 )
//false
alert( '5' === 5 )
In the case of primitive numbers, the isNaN method could also help you.
This is a nuance of Javascript which I've found catches some out. The instanceof of operator will always result in false if the LHS is not an object type.
Note that new String('Hello World') does not result in a string type but is an object. The new operator always results in an object. I see this sort of thing:
function fnX(value)
{
if (typeof value == 'string')
{
//Do stuff
}
}
fnX(new String('Hello World'));
The expectation is that "Do Stuff" will happen but it doesn't because the typeof the value is object.