说到类型,JavaScript 定义了少量的数据类型: null undefined 布尔值 数字 字符串 函数 和 对象。
假如要区分值得类型,无疑 typeof 首选 。如下
1 typeof 1 2 typeof "string" 3 typeof true 4 typeof null 5 typeof undefined 6 typeof {} 7 typeof /\W/ 8 9 10 11 :1 number 12 :2 string 13 :3 boolean 14 :4 object 15 :5 undefined 16 :6 object 17 :7 object
在以面向对象思想设计中,我们往往更希望将类作为类型来对待,这样就可以根据对象所属的类来区分它们。JavaScript 核心内置对象可以通过 class 属性来得到其类型,但是这种方式对于自定义对象来说是不能通过 class 属性来区分对应的类型。
看看下面的实战代码-: 前面章节提到过 class 属性是不能直接获取,只能借助 toString 方式来获取,但是在 部分对象覆盖了 toString 例如 Date Math 等 那可以通过 call 或 apply 达到统一道理
function classOf(o) { if (o == null) return "Null"; if (o === "undefined") return "undefined"; return Object.prototype.toString.call(o).slice(8, -1); }
function User() {} console.log(classOf([])) console.log(classOf(new Date())) console.log(classOf(new RegExp())) console.log(classOf(Math)) console.log(classOf({})); console.log(classOf(new User())) Array Date RegExp Math Object Object
对于自定义对象是没有办法判断其属于什么类型,下面主要介绍下三个属性: instanceof运算符 、constructor属性、构造函数的名字。 但是每种技术都不甚完美。
instanceof 语法格式
操作符左边为实例对象,右边为定义构造函数。
先来看看下面的简单实例:
function A() {}; function B() {}; var a = new A(); console.log(a instanceof A); // true A.prototype = new B(); console.log(a instanceof A); //false
上面主要定义两个构造函数,然后声明一个变量a 再更改A的原型对象指向。 从上面两个输出来看,可以得出,只要 A.prototype 在a的原型链上的对象,不管是直接继承还是间接继承,其都会返回 true;
可以用下图表示:
根据得出结论可以看出,在更该之后其原型 A.prototype 指向新的实例,并不存在于a对象原型链上。 从instanceof操作符右侧虽然制定的构造函数,但是其并不是根据其来进行判断,而是检测的是他们的继承关系。 那么有没有直接判断某个对象是不是继承另一个对象尼?
Object.isPrototypeOf()
function A() {}; function B() {}; var a = new A(); console.log(A.prototype.isPrototypeOf(a)); A.prototype = new B(); console.log(A.prototype.isPrototypeOf(a)); true false
instanceof 与 isPrototypeOf 两者缺点相同,不能通过对象获取类的名称,而是只能判断某个对象是不是存在其原型链上。
注意: 在多框架的情况下, 例如 宿主对象 Array ,在每个框架都拥有一套不同的构造函数,所以在进行A框架-> B框架传递一个Array实例时, 使用 instanceof 会返回false
constructor 属性
顾名思义 获取对象的构造函数。构造函数是类的公共标识,所以判断某个实例是不是属于某个类可以直接使用 constructor属性
先来看看其使用:如下实例
function Person() {} function classOf(o) { var con = o.constructor; switch (con) { case Number: console.log("Number"); break; case String: console.log("String"); break; case Boolean: console.log("Boolean"); break; case Array: console.log("Array"); break; case RegExp: console.log("RegExp"); break; case Date: console.log("Date"); break; case Object: console.log("Object"); break; case Person: console.log("Person"); break; } } classOf(1); classOf("string"); classOf(true); classOf([]); classOf(/\w/); classOf(new Date()); classOf({}); classOf(new Person()); Number String Boolean Array RegExp Date Object Person
注意:这种方式也是存在一个问题,在多个上下文环境的(多框架的情况下),其是不能正确返回的问题。
构造函数名称
在instanceof 和 constructor 在针对多中上下文的情况下,都会出现错误。如果在多框架下,唯一解决办法是可以通过根据构造函数名称而不已构造函数本省作为公共标识。
JavaScript中提供非标准的 name 属性,在某些浏览器中不支持name属性时,可以通过先把函数转换为字符串,再进行比较。 //主要获取函数名称
Function.prototype.getName = function() { return this.name || A.toString().match(/\w+\s*([^()*])/)[1]; }
注意:这种方式有一个问题是,当函数为匿名函数时,会返回空值。
但是如果是对象(除函数外)时,可以利用其 class 属性 ,而 class 属性获取方式为:
function classOf(o) { if (o == null) return "Null"; if (o === "undefined") return "undefined"; return Object.prototype.toString.call(o).slice(8, -1); }
结合上述几种方式,可以用来获取其type名称
function type(o) { var t, c, n; if (o === null) return "Null"; if (o != o) return "NuN"; /** * 通过这种方式:可以区分 */ if ((t = typeof o) !== "object") return t; //可以区分大部分的内置对象 在大多数情况下 都覆写了toString() if ((c = classOf(o)) !== "Object") return c; //自定义对象 并且存在constructor和函数名称属性 if (o.constructor && typeof o.constructor === "function" && (n = o.constructor.getName())) return n; return "Object"; }