在 ECMAScript 规范中,共定义了 7 种数据类型,分为 基本类型 和 引用类型 两大类,以下所示:html
基本类型:String、Number、Boolean、Symbol、Undefined、Null 数组
引用类型:Objectapp
基本类型也称为简单类型,因为其占据空间固定,是简单的数据段,为了便于提高变量查询速度,将其存储在栈中,即按值访问。框架
引用类型也称为复杂类型,因为其值的大小会改变,因此不能将其存放在栈中,不然会下降变量查询速度,所以,其值存储在堆(heap)中,而存储在变量处的值,是一个指针,指向存储对象的内存处,即按址访问。引用类型除 Object 外,还包括 Function 、Array、RegExp、Date 等等。函数
鉴于 ECMAScript 是松散类型的,所以须要有一种手段来检测给定变量的数据类型。对于这个问题,JavaScript 也提供了多种方法,但遗憾的是,不一样的方法获得的结果良莠不齐。spa
下面介绍经常使用的4种方法,并对各个方法存在的问题进行简单的分析。prototype
typeof 是一个操做符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括如下 7 种:number、boolean、symbol、string、object、undefined、function 等。指针
typeof ''; // string 有效 typeof 1; // number 有效 typeof Symbol(); // symbol 有效 typeof true; //boolean 有效 typeof undefined; //undefined 有效 typeof null; //object 无效 typeof [] ; //object 无效 typeof new Function(); // function 有效 typeof new Date(); //object 无效 typeof new RegExp(); //object 无效
有些时候,typeof 操做符会返回一些使人迷惑但技术上却正确的值:htm
其中,null 有属于本身的数据类型 Null , 引用类型中的 数组、日期、正则 也都有属于本身的具体类型,而 typeof 对于这些类型的处理,只返回了处于其原型链最顶端的 Object 类型,没有错,但不是咱们想要的结果。对象
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,若是 A 是 B 的实例,则返回 true,不然返回 false。 在这里须要特别注意的是:instanceof 检测的是原型,咱们用一段伪代码来模拟其内部执行过程:
instanceof (A,B) = { var L = A.__proto__; var R = B.prototype; if(L === R) { // A的内部属性 __proto__ 指向 B 的原型对象 return true; } return false; }
从上述过程能够看出,当 A 的 __proto__ 指向 B 的 prototype 时,就认为 A 就是 B 的实例,咱们再来看几个例子:
[] instanceof Array; // true {} instanceof Object;// true new Date() instanceof Date;// true function Person(){}; new Person() instanceof Person; [] instanceof Object; // true new Date() instanceof Object;// true new Person instanceof Object;// true
咱们发现,虽然 instanceof 可以判断出 [ ] 是Array的实例,但它认为 [ ] 也是Object的实例,为何呢?
咱们来分析一下 [ ]、Array、Object 三者之间的关系:
从 instanceof 可以判断出 [ ].__proto__ 指向 Array.prototype,而 Array.prototype.__proto__ 又指向了Object.prototype,最终 Object.prototype.__proto__ 指向了null,标志着原型链的结束。所以,[]、Array、Object 就在内部造成了一条原型链:
从原型链能够看出,[] 的 __proto__ 直接指向Array.prototype,间接指向 Object.prototype,因此按照 instanceof 的判断规则,[] 就是Object的实例。依次类推,相似的 new Date()、new Person() 也会造成一条对应的原型链 。所以,instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪一种类型。
instanceof 操做符的问题在于,它假定只有一个全局执行环境。若是网页中包含多个框架,那实际上就存在两个以上不一样的全局执行环境,从而存在两个以上不一样版本的构造函数。若是你从一个框架向另外一个框架传入一个数组,那么传入的数组与在第二个框架中原生建立的数组分别具备各自不一样的构造函数。
var iframe = document.createElement('iframe'); document.body.appendChild(iframe); xArray = window.frames[0].Array; var arr = new xArray(1,2,3); // [1,2,3] arr instanceof Array; // false
针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象自己是否为 Array 类型,而不区分该对象在哪一个环境中建立。
if (Array.isArray(value)){ //对数组执行某些操做 }
Array.isArray() 本质上检测的是对象的 [[Class]] 值,[[Class]] 是对象的一个内部属性,里面包含了对象的类型信息,其格式为 [object Xxx] ,Xxx 就是对应的具体类型 。对于数组而言,[[Class]] 的值就是 [object Array] 。
当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,而后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。以下所示:
当执行 var f = new F() 时,F 被当成了构造函数,f 是F的实例对象,此时 F 原型上的 constructor 传递到了 f 上,所以 f.constructor == F
能够看出,F 利用原型对象上的 constructor 引用了自身,当 F 做为构造函数来建立对象时,原型上的 constructor 就被遗传到了新建立的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。这样作的意义是,让新对象在诞生之后,就具备可追溯的数据类型。
一样,JavaScript 中的内置对象在内部构建时也是这样作的:
细节问题:
1. null 和 undefined 是无效的对象,所以是不会有 constructor 存在的,这两种类型的数据须要经过其余方式来判断。
2. 函数的 constructor 是不稳定的,这个主要体如今自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object
为何变成了 Object?
由于 prototype 被从新赋值的是一个 { }, { } 是 new Object() 的字面量,所以 new Object() 会将 Object 原型上的 constructor 传递给 { },也就是 Object 自己。
所以,为了规范开发,在重写对象原型时通常都须要从新给 constructor 赋值,以保证对象实例的类型不被篡改。
toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。
对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其余对象,则须要经过 call / apply 来调用才能返回正确的类型信息。
Object.prototype.toString.call('') ; // [object String] Object.prototype.toString.call(1) ; // [object Number] Object.prototype.toString.call(true) ; // [object Boolean] Object.prototype.toString.call(Symbol()); //[object Symbol] Object.prototype.toString.call(undefined) ; // [object Undefined] Object.prototype.toString.call(null) ; // [object Null] Object.prototype.toString.call(new Function()) ; // [object Function] Object.prototype.toString.call(new Date()) ; // [object Date] Object.prototype.toString.call([]) ; // [object Array] Object.prototype.toString.call(new RegExp()) ; // [object RegExp] Object.prototype.toString.call(new Error()) ; // [object Error] Object.prototype.toString.call(document) ; // [object HTMLDocument] Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
原创发布 @ 一像素 2016.04