基本数据类型篇

86 阅读6分钟

2.数据类型篇

2.1基本数据类型

1. 简单数据类型

一共7种: String, Number,Boolean,Undefind,Null,Symbol,BigInt

2. 复杂数据类型Object

集合引用类型: Object, Array, Map, Weak Map, Set , Weak Set

基本引用类型 Date, RegExp, GlobalMath , 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 的类型标签是 0typeof null 也因此返回 "object"

之后也是为了不得罪人,也就一直保留了这个问题特性。

  • null 是简单类型的值,它有自己的类型 Null,但是 typeof null 返回的却是 object

在 JavaScript 最初的实现中,值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0typeof null 也因此返回 "object"

之后也是为了不得罪人,也就一直保留了这个问题特性。

2.instanceof

原理通过对比对象那个实例的 constructor 属性 指向用于创建当前对象的函数(constructor 属性是在构造函数的 prototype 上定义的)

Array.prototype.constructor === Array; // true
[].constructor === Array; // true// 为什么原始值也有 constructor ? 可以看 typeof 那一节有解释
''.constructor === String // true// null 和 undefined 不存在 constructor 的

为什么说instanceof 依然不是最可靠的?

  1. 构造函数的prototype是可以进行改变的,一旦发生改变,类型判断就会出错

    function F() {}
    var f = new F;
    F.prototype = {}
    f instanceof F  // falsefunction 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
  1. 多个构造函数的原形,无法精确判断

    function foo() {}
    foo instanceof Function  // true
    foo instanceof Object    // true

注意点: instanceof 是操作符,所以要注意操作符的优先级,例如检测对象不是某个构造函数的实例时:

if (!(mycar instanceof Car)) {
  // ...
}
​
// 而不是使用 if (!mycar instanceof Car),逻辑非的优先级高于 instanceof

另外,不能用于检测nullundefined, 会抛错。

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
  1. 图片.png

图片.png

原理的探究,object.prototype.tostring()是一个在toString方法被调用时,会执行下面的操作步骤:

  1. 获取this对象的[[Class]]属性的值.
  2. 计算出三个字符串"[object ", 第一步的操作结果Result(1), 以及 "]"连接后的新字符串.
  3. 返回第二步的操作结果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个值.