Is there a JavaScript equivalent of Java\'s class.getName()
?
You can use the "instanceof" operator to determine if an object is an instance of a certain class or not. If you do not know the name of an object's type, you can use its constructor property. The constructor property of objects, is a reference to the function that is used to initialize them. Example:
function Circle (x,y,radius) {
this._x = x;
this._y = y;
this._radius = raduius;
}
var c1 = new Circle(10,20,5);
Now c1.constructor is a reference to the Circle()
function.
You can alsow use the typeof
operator, but the typeof
operator shows limited information. One solution is to use the toString()
method of the Object global object. For example if you have an object, say myObject, you can use the toString()
method of the global Object to determine the type of the class of myObject. Use this:
Object.prototype.toString.apply(myObject);
Lodash has many isMethods so if you're using Lodash maybe a mixin like this can be useful:
// Mixin for identifying a Javascript Object
_.mixin({
'identify' : function(object) {
var output;
var isMethods = ['isArguments', 'isArray', 'isArguments', 'isBoolean', 'isDate', 'isArguments',
'isElement', 'isError', 'isFunction', 'isNaN', 'isNull', 'isNumber',
'isPlainObject', 'isRegExp', 'isString', 'isTypedArray', 'isUndefined', 'isEmpty', 'isObject']
this.each(isMethods, function (method) {
if (this[method](object)) {
output = method;
return false;
}
}.bind(this));
return output;
}
});
It adds a method to lodash called "identify" which works as follow:
console.log(_.identify('hello friend')); // isString
Plunker: http://plnkr.co/edit/Zdr0KDtQt76Ul3KTEDSN
Here is an implementation based on the accepted answer:
/**
* Returns the name of an object's type.
*
* If the input is undefined, returns "Undefined".
* If the input is null, returns "Null".
* If the input is a boolean, returns "Boolean".
* If the input is a number, returns "Number".
* If the input is a string, returns "String".
* If the input is a named function or a class constructor, returns "Function".
* If the input is an anonymous function, returns "AnonymousFunction".
* If the input is an arrow function, returns "ArrowFunction".
* If the input is a class instance, returns "Object".
*
* @param {Object} object an object
* @return {String} the name of the object's class
* @see <a href="https://stackoverflow.com/a/332429/14731">https://stackoverflow.com/a/332429/14731</a>
* @see getFunctionName
* @see getObjectClass
*/
function getTypeName(object)
{
const objectToString = Object.prototype.toString.call(object).slice(8, -1);
if (objectToString === "Function")
{
const instanceToString = object.toString();
if (instanceToString.indexOf(" => ") != -1)
return "ArrowFunction";
const getFunctionName = /^function ([^(]+)\(/;
const match = instanceToString.match(getFunctionName);
if (match === null)
return "AnonymousFunction";
return "Function";
}
// Built-in types (e.g. String) or class instances
return objectToString;
};
/**
* Returns the name of a function.
*
* If the input is an anonymous function, returns "".
* If the input is an arrow function, returns "=>".
*
* @param {Function} fn a function
* @return {String} the name of the function
* @throws {TypeError} if {@code fn} is not a function
* @see getTypeName
*/
function getFunctionName(fn)
{
try
{
const instanceToString = fn.toString();
if (instanceToString.indexOf(" => ") != -1)
return "=>";
const getFunctionName = /^function ([^(]+)\(/;
const match = instanceToString.match(getFunctionName);
if (match === null)
{
const objectToString = Object.prototype.toString.call(fn).slice(8, -1);
if (objectToString === "Function")
return "";
throw TypeError("object must be a Function.\n" +
"Actual: " + getTypeName(fn));
}
return match[1];
}
catch (e)
{
throw TypeError("object must be a Function.\n" +
"Actual: " + getTypeName(fn));
}
};
/**
* @param {Object} object an object
* @return {String} the name of the object's class
* @throws {TypeError} if {@code object} is not an Object
* @see getTypeName
*/
function getObjectClass(object)
{
const getFunctionName = /^function ([^(]+)\(/;
const result = object.constructor.toString().match(getFunctionName)[1];
if (result === "Function")
{
throw TypeError("object must be an Object.\n" +
"Actual: " + getTypeName(object));
}
return result;
};
function UserFunction()
{
}
function UserClass()
{
}
let anonymousFunction = function()
{
};
let arrowFunction = i => i + 1;
console.log("getTypeName(undefined): " + getTypeName(undefined));
console.log("getTypeName(null): " + getTypeName(null));
console.log("getTypeName(true): " + getTypeName(true));
console.log("getTypeName(5): " + getTypeName(5));
console.log("getTypeName(\"text\"): " + getTypeName("text"));
console.log("getTypeName(userFunction): " + getTypeName(UserFunction));
console.log("getFunctionName(userFunction): " + getFunctionName(UserFunction));
console.log("getTypeName(anonymousFunction): " + getTypeName(anonymousFunction));
console.log("getFunctionName(anonymousFunction): " + getFunctionName(anonymousFunction));
console.log("getTypeName(arrowFunction): " + getTypeName(arrowFunction));
console.log("getFunctionName(arrowFunction): " + getFunctionName(arrowFunction));
//console.log("getFunctionName(userClass): " + getFunctionName(new UserClass()));
console.log("getTypeName(userClass): " + getTypeName(new UserClass()));
console.log("getObjectClass(userClass): " + getObjectClass(new UserClass()));
//console.log("getObjectClass(userFunction): " + getObjectClass(UserFunction));
//console.log("getObjectClass(userFunction): " + getObjectClass(anonymousFunction));
//console.log("getObjectClass(arrowFunction): " + getObjectClass(arrowFunction));
console.log("getTypeName(nativeObject): " + getTypeName(navigator.mediaDevices.getUserMedia));
console.log("getFunctionName(nativeObject): " + getFunctionName(navigator.mediaDevices.getUserMedia));
We only use the constructor property when we have no other choice.
Here is a solution that I have come up with that solves the shortcomings of instanceof. It can check an object's types from cross-windows and cross-frames and doesn't have problems with primitive types.
function getType(o) {
return Object.prototype.toString.call(o).match(/^\[object\s(.*)\]$/)[1];
}
function isInstance(obj, type) {
var ret = false,
isTypeAString = getType(type) == "String",
functionConstructor, i, l, typeArray, context;
if (!isTypeAString && getType(type) != "Function") {
throw new TypeError("type argument must be a string or function");
}
if (obj !== undefined && obj !== null && obj.constructor) {
//get the Function constructor
functionConstructor = obj.constructor;
while (functionConstructor != functionConstructor.constructor) {
functionConstructor = functionConstructor.constructor;
}
//get the object's window
context = functionConstructor == Function ? self : functionConstructor("return window")();
//get the constructor for the type
if (isTypeAString) {
//type is a string so we'll build the context (window.Array or window.some.Type)
for (typeArray = type.split("."), i = 0, l = typeArray.length; i < l && context; i++) {
context = context[typeArray[i]];
}
} else {
//type is a function so execute the function passing in the object's window
//the return should be a constructor
context = type(context);
}
//check if the object is an instance of the constructor
if (context) {
ret = obj instanceof context;
if (!ret && (type == "Number" || type == "String" || type == "Boolean")) {
ret = obj.constructor == context
}
}
}
return ret;
}
isInstance requires two parameters: an object and a type. The real trick to how it works is that it checks if the object is from the same window and if not gets the object's window.
Examples:
isInstance([], "Array"); //true
isInstance("some string", "String"); //true
isInstance(new Object(), "Object"); //true
function Animal() {}
function Dog() {}
Dog.prototype = new Animal();
isInstance(new Dog(), "Dog"); //true
isInstance(new Dog(), "Animal"); //true
isInstance(new Dog(), "Object"); //true
isInstance(new Animal(), "Dog"); //false
The type argument can also be a callback function which returns a constructor. The callback function will receive one parameter which is the window of the provided object.
Examples:
//"Arguments" type check
var args = (function() {
return arguments;
}());
isInstance(args, function(w) {
return w.Function("return arguments.constructor")();
}); //true
//"NodeList" type check
var nl = document.getElementsByTagName("*");
isInstance(nl, function(w) {
return w.document.getElementsByTagName("bs").constructor;
}); //true
One thing to keep in mind is that IE < 9 does not provide the constructor on all objects so the above test for NodeList would return false and also a isInstance(alert, "Function") would return false.
Use class.name
. This also works with function.name
.
class TestA {}
console.log(TestA.name); // "TestA"
function TestB() {}
console.log(TestB.name); // "TestB"
The kind() function from Agave.JS will return:
It works on all JS objects and primitives, regardless of how they were created, and doesn't have any surprises. Examples:
kind(37) === 'Number'
kind(3.14) === 'Number'
kind(Math.LN2) === 'Number'
kind(Infinity) === 'Number'
kind(Number(1)) === 'Number'
kind(new Number(1)) === 'Number'
kind(NaN) === 'NaN'
kind('') === 'String'
kind('bla') === 'String'
kind(String("abc")) === 'String'
kind(new String("abc")) === 'String'
kind(true) === 'Boolean'
kind(false) === 'Boolean'
kind(new Boolean(true)) === 'Boolean'
kind([1, 2, 4]) === 'Array'
kind(new Array(1, 2, 3)) === 'Array'
kind({a:1}) === 'Object'
kind(new Object()) === 'Object'
kind(new Date()) === 'Date'
kind(function(){}) === 'Function'
kind(new Function("console.log(arguments)")) === 'Function'
kind(Math.sin) === 'Function'
kind(undefined) === 'undefined'
kind(null) === 'null'