Easy way to check if a variable is a string? - javascript

This question is a spin-off of [] is an instance of Array but "" isn't of String
Given that
"" instanceof String; /* false */
String() instanceof String; /* false */
new String() instanceof String; /* true */
and
typeof "" === "string"; /* true */
typeof String() === "string"; /* true */
typeof new String() === "string"; /* false */
Then, if I have a variable abc and I want to know if it's a string, I can do
if(typeof abc === "string" || abc instanceof String){
// do something
}
Is there a simpler, shorter and native way of doing this, or must I create my own function?
function isStr(s){
return typeof s === "string" || s instanceof String;
}
if(isStr(abc)){
// do something
}

I think Object.prototype.toString.call(a) === "[object String]" is the shortest/nativest way of doing this

you are correct:
typeof myVar == 'string' || myVar instanceof String;
is one of the best ways to check if a variable is a string.

You may be confused because [] is an array initialiser (often called an array literal) that is defined as creating an Array object, whereas '' is a string literal that is defined as creating a string primitive.
A primitive isn't an instance of any kind of object, though it may be coerced to a related object for convenience.
A more important question is why an isString function should return true for both string primitives and string objects? The use of string objects is (extremely?) rare, I would have thought that their use would infer special treatment and that you would want to differentiate between the two and not treat them the same.
It's far more common to ignore the Type of a variable and, where it's Type might vary, unconditionally convert it to the required Type, e.g. if you want a string primitive:
function foo(s) {
s = String(s); // s is guaranteed to be a string primitive
...
}
The exception is where functions are overloaded and have different behaviour depending on whether a particular argument is a Function, Object or whatever. Such overloading is generally not considered a good idea, but many javascript libraries are dependent on it. In those cases, passing a String object rather than a string primitive may have unexpected consequences.

Related

In JavaScript, where do the constant strings returned from typeof come from?

I've seen a lot of places that do a string comparison with the typeof keyword and it returns what are obviously constant strings, such as string, object, undefined etc. I would expect to see those defined as constants somewhere but it doesn't seem to be the case (they are listed on MDN here). I don't like to see duplication of strings being used in code as they seem magic. The current solution is to define them as const elsewhere in code myself but it seems JS knows these anyway so why can't I get to them? (or can I?)
magic string:
if(typeof myObject === 'object') {}
This seems a little better, since no duplication of magic string, but I don't see why I should have to define the const:
const TYPE_OBJECT = 'object';
if(typeof myObject === TYPE_OBJECT) {}
if(typeof myOtherObject === TYPE_OBJECT) {}
Or, this seems clumsy too:
if(typeof myObject === Object.name.toLowerCase()) {}
if(typeof "hello" === String.name.toLowerCase()) {}
Is there a better way or are these constants built in somewhere?
These string constants are not available as predefined constants. But if you don't want to specify those string literals yourself, you can do the inverse, and apply typeof to some basic values you provide, and define the constants with those returned strings. For instance:
const TYPES = Object.freeze({
OBJECT: typeof {},
UNDEFINED: typeof undefined,
BOOLEAN: typeof true,
NUMBER: typeof 1,
STRING: typeof "",
FUNCTION: typeof eval,
SYMBOL: typeof Symbol()
});
console.log(TYPES);
// Use it:
let myObject = { "test": 123 };
console.log(typeof myObject === TYPES.OBJECT); // true
I think the presumption that these strings have to come from somewhere is a fallacy. JavaScript is a spec and, in this regard, all there is to it that typeof is an operator that returns a string which can have one of the predefined values listed in the spec. Where these come from is thus implementation-dependent.
Furthermore, while things like typeof myObject === Object.name.toLowerCase() or typeof myObject === TYPE_OBJECT may seem better than using a "bare" string, typeof myObject === 'object' is a well-known pattern. By using something different, you are reducing the readability of your code, as the other two variants are guaranteed to raise some brows and prompt people to ask themselves what this line does.
Had Symbol existed in JS from the very start, I believe typeof would've just returned a Symbol. Sadly, for backwards-compatibility, it has to return a string.

Why is 'true instanceof Boolean' equal to false in JavaScript [duplicate]

This question already has answers here:
Why does instanceof return false for some literals?
(10 answers)
Closed 7 years ago.
The following indicates the expression "true instanceof Boolean" evaluates to false. Why would this expression evaluate to false?
$(document).ready(function() {
var $result = $('#result');
if(true instanceof Boolean) {
$result.append('I\'m a Boolean!');
} else {
$result.append('I\'m something other than a Boolean!');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result"></div>
The value true is a boolean primitive — that's "boolean" with a lower-case "b". It is not an object.
Similarly, "hello" is not an instance of String, because a string primitive is not an object either.
You can test for primitive types with the typeof operator.
The distinction between primitive values and object values can be a little confusing in JavaScript because the language implicitly wraps primitives in appropriate object wrappers when primitives are used like object references. That's why you can write
var length = "hello world".length;
The string is implicitly converted to a String instance for that . operation to work. You can even do that with a boolean:
var trueString = true.toString();
It happens with numbers too, though the syntax gets in the way sometimes:
var failure = 2.toString(); // an error
var success = (2).toString(); // works
The Boolean object is an object wrapper for a boolean value.
So the boolean value and the wrapper object are not the same thing.
Also, boolean objects are verboten. In other words it isn't good style to ever use a Boolean object.

Using 'in' when the object may be a string

I was looking up how to check if a variable in JavaScript is an array, but then as the SO page was loading, I thought of a solution to the problem. Looking through the answers, I found that none of them had thought of this simple answer: Just check for the methods we need to use on the array, so that it still works for any user defined types that implement the same methods. Being the helpful person I am, I thought I'd submit my answer for future people trying to solve the same problem..But after testing it, I found it does not work.
function print(object) {
if ('map' in object) { // If the object implements map, treat it like an array
object.map(function(current) {
console.log(current);
});
} else { // Otherwise, treat it as a string
console.log(object);
}
}
Now, this works fine when I call it with an array, but if I use a string it fails. Since strings are objects in javascript, why shouldn't the 'in' keyword work for them? Is there any way to implement this that is as simple as what it currently is?
You can access the property and test its type:
if (object != null && typeof object.map === 'function')
object.map(...);
// ...
Since strings are objects in javascript, why shouldn't the 'in' keyword work for them?
Not quite. There is a difference between a primitive string value and a String instance object.
typeof "foo"; // "string"
typeof new String(); // "object"
Primitive values don't have properties themselves, which is why the in operator throws an Error when used on them. They will, however, be temporarily boxed into Objects by property accessors:
var a = "foo";
a.bar = 'bar'; // no error, but...
console.log(a.bar); // undefined
String.prototype.baz = function () {
return this + this;
};
console.log(a.baz()); // "foofoo"
I suppose you could do something like this:
var y = function (o) {
if (typeof o != "string" && "map" in o)
console.log("not a string"); //your code here
else
console.log("It's a string!"); //your code here
}
I tested this with var x = "hello" and it worked. Because && short circuits in JavaScript, it will stop at typeof o != "string", which is good, since "map" in o fails for strings. Note the intentional use of lowercase, which is for primitives; Strings are objects. I'm not exactly sure if this is what you were aiming for.

typeof for RegExp

Is there anyway to detect if a JavaScript object is a regex?
For example, I would like to do something like this:
var t = /^foo(bar)?$/i;
alert(typeof t); //I want this to return "regexp"
Is this possible?
Thanks!
EDIT: Thanks for all the answers. It seems I have two very good choices:
obj.constructor.name === "RegExp"
or
obj instanceof RegExp
Any major pros/cons to either method?
Thanks again!
You can use instanceof operator:
var t = /^foo(bar)?$/i;
alert(t instanceof RegExp);//returns true
In fact, that is almost the same as:
var t = /^foo(bar)?$/i;
alert(t.constructor == RegExp);//returns true
Keep in mind that as RegExp is not a primitive data type, it is not possible to use typeof operator which could be the best option for this question.
But you can use this trick above or others like duck type checking, for example, checking if such object has any vital methods or properties, or by its internal class value (by using {}.toString.call(instaceOfMyObject)).
alert( Object.prototype.toString.call( t ) ); // [object RegExp]
This is the way mentioned in the specification for getting the class of object.
From ECMAScript 5, Section 8.6.2 Object Internal Properties and Methods:
The value of the [[Class]] internal property is defined by this specification for every kind of built-in object. The value of the [[Class]] internal property of a host object may be any String value except one of "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String". The value of a [[Class]] internal property is used internally to distinguish different kinds of objects. Note that this specification does not provide any means for a program to access that value except through Object.prototype.toString (see 15.2.4.2).
A RegExp is a class of object defined in the spec at Section 15.10 RegExp(RegularExpression)Objects:
A RegExp object contains a regular expression and the associated flags.
Give the .constructor property a whirl:
> /^foo(bar)?$/i.constructor
function RegExp() { [native code] }
> /^foo(bar)?$/i.constructor.name
"RegExp"
> /^foo(bar)?$/i.constructor == RegExp
true
From underscore.js
// Is the given value a regular expression?
_.isRegExp = function(obj) {
return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
};
Works in google chrome:
x = /^foo(bar)?$/i;
x == RegExp(x); // true
y = "hello";
y == RegExp(y); // false
"Regexp" is not a native Javascript type. Most of the above answers tell you how to accomplish your task, but not why. Here's why.
There is no absolute way of checking this, so far the best answer is
var t = /^foo(bar)?$/i;
alert(t instanceof RegExp);//returns true
but there is one down side to this approach and that's it will return false if the regular expression object is commeing from an other window.
Here are two ways:
/^\/.*\/$/.test(/hi/) /* test regexp literal via regexp literal */
/^\/.*\/$/.test(RegExp("hi") ) /* test RegExp constructor via regexp literal */
RegExp("^/" + ".*" + "/$").test(/hi/) /* test regexp literal via RegExp constructor */
RegExp("^/" + ".*" + "/$").test(RegExp("hi") ) /* test RegExp constructor via RegExp constructor */
delete RegExp("hi").source /* test via deletion of the source property */
delete /hi/.global /* test via deletion of the global property */
delete /hi/.ignoreCase /* test via deletion of the ignoreCase property */
delete RegExp("hi").multiline /* test via deletion of the multiline property */
delete RegExp("hi").lastIndex /* test via deletion of the lastIndex property */
If a string literal is delimited by the regexp backslash delimiter, the regexp self test will fail.
If Object.seal or Object.freeze are run on a user-defined object, and that object also has all of the aforementioned properties, the delete statement will return a false positive.
References
ECMAScript 5, Section 15.10.4.1 new RegExp(pattern, flags)
What are some use-cases of when delete has been very useful in JavaScript?
Why is this configurable property not deletable?
Webkit JavaScriptCore source: ObjectConstructor.cpp
Object Properties in JavaScript
I was looking for typeof regex because I tried use it in type definition for TypeScript props on a function.
Then I do the next:
const RegexType = /$/;
type paramProps = {
regexParam: typeof RegexType;
}
You can test here:
const RegexType = /$/;
const t = /^foo(bar)?$/i;
console.log(typeof t == typeof RegexType) //true

Are string literals objects or not?

Trying to get my JavaSscript fundamentals strong. So the question is about string literals. Aren't they Objects? If your answer is 'yes' then my question is why is instanceof returning false?
> var s = new String
> s.constructor.toString()
function String() { [native code] }
> typeof s
object
> s instanceof String
true
> s instanceof Object
true
> s instanceof Number
false
So far so good.
> typeof 'c'
string
> 'c' instanceof Object
false
> 'c' instanceof String
false
> 'c'.length
1
> 'c'.charAt(0)
c
> 'c'.constructor.toString()
function String() { [native code] }
String literals are primitives (String values), String objects can be created with the String constructor in a new expression:
"foo" instanceof String // false
new String("foo") instanceof String // true
Edit: Something that seems to be confusing (by looking at the accepted answer here), is that you can still access properties defined on the prototype objects of primitive values, for example:
"foo".indexOf == String.prototype.indexOf // true
"foo".match == String.prototype.match // true
String.prototype.test = true;
"foo".test // true
true.toString == Boolean.prototype.toString
(3).toFixed == Number.prototype.toFixed // true
// etc...
The reason of that relies on the Property Accessors, the dot notation . and the bracket notation [].
Let's give a look to the algorithm in the ECMA-262 specification:
The production MemberExpression : MemberExpression [ Expression ] (or MemberExpression . Identifier) is evaluated as follows:
Evaluate MemberExpression.
Call GetValue(Result(1)).
Evaluate Expression.
Call GetValue(Result(3)).
Call ToObject(Result(2)).
Call ToString(Result(4)).
Return a value of type Reference whose base object is Result(5) and whose property name is Result(6).
In the Step 5, the ToObject internal operator type-converts the MemberExpression to object, depending on it's type.
The primitives are converted to Objects without noticing, and that's why you can access the properties defined on the prototype.
Great explanation of this here. Copied for reference below.
That's because those things are primitives, and unless they need to be used as objects (when you are calling methods on them, for example) they remain so. The only time they "become" objects is when they need to be wrapped. If you are familiar with the concept of "boxing" in .NET, then think of it in that way.
Here is an example - take a look at this code:
Number.prototype.times = function(func) {
for(var index = 1; index <= this; index++) {
func(index);
}
};
So, the following code will fail:
3.times(print); // assume 'print' writes to standard out
3, by itself is a primitive. That said, the following will work:
(3).times(print); // assume 'print' writes to standard out
That would display the numbers 1, 2, and 3. Because of the parenthesis, the JavaScript interpreter will temporarily wrap the primitive 3 in a Number object, call the method, and then garbage collect the object since it isn't needed any longer.
Anyway, a full discussion of this can be found in "JavaScript: The Definitive Guide."

Categories