2.数据类型篇
2.1基本数据类型
1. 简单数据类型
一共7种: String
, Number
,Boolean
,Undefind
,Null
,Symbol
,BigInt
2. 复杂数据类型Object
集合引用类型: Object
, Array
, Map
, Weak Map
, Set
, Weak Set
基本引用类型 Date
, RegExp
, Global
和 Math
, Function
2.2 类型判断
1. typeof
typeof 37 === 'number'
typeof NaN === 'number'
typeof 42n === 'bigint'
typeof 'bla' === 'string'
typeof undefine === 'undefine'
typeof Symbol('foo') === 'symbol'
typeof false === 'boolean'
typeof function () {} === 'function'
但是对引用值的作用不大,因为无论什么类型的对象(除了 Function
类型),使用typeof
都会返回 "object"
。但是我们往往关心的不是这个值是不是对象,而是这个值是什么类型的对象。
typeof { a: 1} === 'Object'
typeof [1,2,4] === 'Object'
typeof new Date() === 'object'
typeof /regex/ === 'object'
null
是简单类型的值,它有自己的类型Null
,但是typeof null
返回的却是object
在 JavaScript 最初的实现中,值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是
0
。由于null
代表的是空指针(大多数平台下值为 0x00),因此,null
的类型标签是0
,typeof null
也因此返回"object"
。之后也是为了不得罪人,也就一直保留了这个问题特性。
null
是简单类型的值,它有自己的类型Null
,但是typeof null
返回的却是object
在 JavaScript 最初的实现中,值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是
0
。由于null
代表的是空指针(大多数平台下值为 0x00),因此,null
的类型标签是0
,typeof null
也因此返回"object"
。之后也是为了不得罪人,也就一直保留了这个问题特性。
2.instanceof
原理通过对比对象那个实例的 constructor
属性 指向用于创建当前对象的函数(constructor
属性是在构造函数的 prototype
上定义的)
Array.prototype.constructor === Array; // true
[].constructor === Array; // true
// 为什么原始值也有 constructor ? 可以看 typeof 那一节有解释
''.constructor === String // true
// null 和 undefined 不存在 constructor 的
为什么说instanceof
依然不是最可靠的?
-
构造函数的prototype是可以进行改变的,一旦发生改变,类型判断就会出错
function F() {} var f = new F; F.prototype = {} f instanceof F // false function F() {} var f = new F; f.__proto__ = {}; f instanceof F // false
2.不适用与原始值
var simpleStr = "This is a simple string";
var newStr = new String("String created with constructor");
simpleStr instanceof String; // 返回 false, 非对象实例,因此返回 false
newStr instanceof String; // 返回 true
-
多个构造函数的原形,无法精确判断
function foo() {} foo instanceof Function // true foo instanceof Object // true
注意点: instanceof
是操作符,所以要注意操作符的优先级,例如检测对象不是某个构造函数的实例时:
if (!(mycar instanceof Car)) {
// ...
}
// 而不是使用 if (!mycar instanceof Car),逻辑非的优先级高于 instanceof
另外,不能用于检测null
和undefined
, 会抛错。
3. Object.prototype.toString.call()
Object.prototype.toString.call('foo'); // "[object String]"
Object.prototype.toString.call([1, 2]); // "[object Array]"
Object.prototype.toString.call(3); // "[object Number]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null); // "[object Null]"
// ...
var o = new Object;
console.log(o[Symbol.toStringTag]); // undefined
原理的探究,object.prototype.tostring()是一个在toString方法被调用时,会执行下面的操作步骤:
- 获取this对象的[[Class]]属性的值.
- 计算出三个字符串"[object ", 第一步的操作结果Result(1), 以及 "]"连接后的新字符串.
- 返回第二步的操作结果Result(2).
[[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性.在规范中,[[Class]]是这么定义的
内部属性 描述 [[Class]] 一个字符串值,表明了该对象的类型. 然后给了一段解释:
所有内置对象的[[Class]]属性的值是由本规范定义的.所有宿主对象的[[Class]]属性的值可以是任意值,甚至可以是内置对象使用过的[[Class]]属性的值.[[Class]]属性的值可以用来判断一个原生对象属于哪种内置类型.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值(查看 15.2.4.2).
也就是说,把Object.prototype.toString方法返回的字符串,去掉前面固定的"[object "和后面固定的"]",就是内部属性[[class]]的值,也就达到了判断对象类型的目的.jQuery中的工具方法$.type(),就是干这个的.
在ES3中,规范文档并没有总结出[[class]]内部属性一共有几种,不过我们可以自己统计一下,原生对象的[[class]]内部属性的值一共有10种.分别是:"Array", "Boolean", "Date", "Error", "Function", "Math", "Number", "Object", "RegExp", "String".
ECMAScript 5
在ES5.1中,除了规范写的更详细一些以外,Object.prototype.toString方法和[[class]]内部属性的定义上也有一些变化,Object.prototype.toString方法的规范如下:
15.2.4.2 Object.prototype.toString ( )
在toString方法被调用时,会执行下面的操作步骤:
如果this的值为undefined,则返回"[object Undefined]".
如果this的值为null,则返回"[object Null]".
让O成为调用ToObject(this)的结果.
让class成为O的内部属性[[Class]]的值.
返回三个字符串"[object ", class, 以及 "]"连接后的新字符串.
可以看出,比ES3多了1,2,3步.第1,2步属于新规则,比较特殊,因为"Undefined"和"Null"并不属于[[class]]属性的值,需要注意的是,这里和严格模式无关(大部分函数在严格模式下,this的值才会保持undefined或null,非严格模式下会自动成为全局对象).第3步并不算是新规则,因为在ES3的引擎中,也都会在这一步将三种原始值类型转换成对应的包装对象,只是规范中没写出来.ES5中,[[Class]]属性的解释更加详细:
所有内置对象的[[Class]]属性的值是由本规范定义的.所有宿主对象的[[Class]]属性的值可以是除了"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String"之外的的任何字符串.[[Class]]内部属性是引擎内部用来判断一个对象属于哪种类型的值的.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值(查看 15.2.4.2).
和ES3对比一下,第一个差别就是[[class]]内部属性的值多了两种,成了12种,一种是arguments对象的[[class]]成了"Arguments",而不是以前的"Object",还有就是多个了全局对象JSON,它的[[class]]值为"JSON".第二个差别就是,宿主对象的[[class]]内部属性的值,不能和这12种值冲突,不过在支持ES3的浏览器中,貌似也没有发现哪些宿主对象故意使用那10个值.