在写代码的过程中 我们经常会遇到这样一个问题–如何获得某一个对象的类型名。
JavaScript是一种弱类型的语言,通常,我们获取一个对象的类型名会想到typeof方法。但是,当我们想要获得一个自定义类型的兑现的类型名的时候,typeof就有点不够看了。我们先来做一个简单的测试。
<!DOCTYPE html>
<html><head>
<script>
/*一个叫做Student的构造函数*/
function Student (argument) {
// body...
}
/*new一个Student对象*/
var stu = new Student("argument");
console.log(typeof stu);
/*控制台打印的结果是:object*/
</script>
</head>
<body></body></html>
我们虽然知道了stu是一个对象,但是我们却不知道它究竟是一个怎样的对象,所以我们还需要更深层次的处理。
那到底需要怎么做呢?我们知道,在js里,每一个对象都有一个constructor的属性,这个属性直接连接到该对象的构造函数。也就是说,我们可以通过对象的constructor属性来找到它所对应的构造函数。先来看一段代码:
<!DOCTYPE html>
<html><head>
<script>
/*一个叫做Student的构造函数*/
function Student (argument) {
// body...
}
/*new一个Student对象*/
var stu = new Student("argument");
/*这里改成了constructor*/
console.log(stu.constructor);
/*
* 控制台打印结果:
* function Student(argument) {
* // body...
* }
*/
</script>
</head>
<body></body></html>
这个结果是不是很熟悉呢?没错,这个就是我们一开始定义的Student的构造函数。也就是说,对象的constructor属性就是这个对象的构造函数。当我们得到了构造函数之后,得到它的类型名也就轻而易举了,无非就是字符串的操作。
先提供一种最基本的方法:
<!DOCTYPE html>
<html><head>
<script>
/*一个叫做Student的构造函数*/
function Student (argument) {
// body...
}
/*new一个Student对象*/
var stu = new Student("argument");
var str = stu.constructor + '';
var start = str.replace("function","|").indexOf('|');
if(start != -1){
var end = str.replace("function","|").indexOf("(");
if(end != -1){ console.log(str.replace("function","|").slice(start+2,end));
}
}
/*
*控制台返回结果:
*Student
*/
</script>
</head>
<body></body></html>
这样,我们就得到了对象的类型名。
当然,这个方法一看就很臃肿,太丑了。我们对它做一些优化。
var start = str.replace("function","|").indexOf('|');
var end = str.replace("function","|").indexOf("(");
if(start != -1 && end != -1){
console.log(str.replace("function","|").slice(start+2,end));
}
这样看起来稍微舒服了一点,但是频繁的操作字符串并不是一个好的方法,我们可以通过正则表达式来完成这个操作。
<!DOCTYPE html>
<html><head>
<script>
/*一个叫做Student的构造函数*/
function Student (argument) {
// body...
}
/*new一个Student对象*/
var stu = new Student("argument");
var str = stu.constructor + '';
var r = /function (.+?)\(/;
var name = r.exec(str)[1];
console.log(name);
/*
*控制台打印结果:
*Student
*/
</script>
</head>
<body></body></html>
用正则表达式来处理字符串会更简单一点,但是需要你对正则有一个很好的掌握。
看到这里,有很多人会一脸不屑,我用一个“函数名.name”就可以解决的事你非要洋洋洒洒写这么一堆,看不起你。
的确,js中的函数有一个叫name的属性,可以通过这个属性直接得到函数名,constructor也是一个函数,当然他也就有这个name属性,我们先来看一下:
<!DOCTYPE html>
<html><head>
<script>
/*一个叫做Student的构造函数*/
function Student (argument) {
// body...
}
/*new一个Student对象*/
var stu = new Student("argument");
var str = stu.constructor + '';
console.log(stu.constructor.name);
/*
*控制台输出:
*Student
*/
</script>
</head>
<body></body></html>
哎呀,原来这么简单啊。很遗憾的告诉你,这个name属性,IE8不支持,哈哈哈哈哈哈哈。
为了兼容不同的浏览器,我们需要自己封装一个方法,先来看一个代码:
<!DOCTYPE html>
<html><head>
<script>
/*一个叫做Student的构造函数*/
function Student (argument) {
// body...
}
/*new一个Student对象*/
var stu = new Student("argument");
function getFnName(fn){
/*确保传进来的参数是一个函数*/
if(typeof fn !== "function") return;
if(fn.name){
return fn.name;
}else{
var r = /function (.+?)\(/;
return r.exec(fn + "")[1];
}
}
console.log(getFnName(stu.constructor));
/*
*控制台输出
*Student
*/
</script>
</head>
<body></body></html>
这样就可以通过getFnName方法来得到对象的类型名了,并且适用于各种浏览器。
当然,这个方法还很粗糙,我们做一些修改:
<!DOCTYPE html>
<html><head>
<script>
/*一个叫做Student的构造函数*/
function Student (argument) {
// body...
}
/*new一个Student对象*/
var stu = new Student("argument");
function getFnName(fn){
return typeof fn !== "function" ?
undefined:
fn.name ||
/function (.+?)\(/.exec(fn + "")[1];
}
console.log(getFnName(stu.constructor));
/*
*控制台输出
*Student
*/
</script>
</head>
<body></body></html>
这样这个方法就完成了