数据类型

记录些关于 JavaScript 数据类型的知识点

note

JavaScript 中的数据类型分为基本类型和引言类型。其中,基本类型包括 NullUndefinedBooleanStringSymbolBigInt;引用类型指的是 Object

1️⃣ nullundefined 的区别?#

定义#

Javascript 中,null 是一个表示“无”的对象,转为数值时为 0;undefined 是一个表示“无”的原始值,转为数值时为 NaN。

应用#

null 表示"没有对象",即该处不应该有值。典型用法是:

1、作为函数的参数,表示该函数的参数不是对象。

2、作为对象原型链的终点。

Object.getPrototypeOf(Object.prototype)
// null

undefined 表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:

1、变量被声明了,但是未被赋值,此时为 undefined

2、调用函数时,应该提供的参数没有提供,该参数等于 undefined

3、对象没有被赋值的属性,该属性的值为 undefined

4、函数没有返回值时,默认值为 undefined

var i;
i // undefined
function f(x){console.log(x)}
f() // undefined
var o = new Object();
o.p // undefined
var x = f();
x // undefined

2️⃣ 判空应该注意什么?#

JavaScript 中有 5 种空值和假值,分别是 undefined、null、false、""、0。

逻辑或操作符 (||会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 || 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,''0)时

let a = 0;
console.log(a || '/'); // '/'

空值合并操作符??)是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。

let a = 0;
console.log(a??'/'); // 0

3️⃣ BigInt#

BigInt 是一种内置对象,它提供了一种方法来表示大于 2^53 - 1 的整数。这原本是 Javascript中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数

BigInt 在某些方面类似于 Number ,但是也有几个关键的不同点:不能用于 Math 对象中的方法;不能和任何 Number 实例混合运算,两者必须转换成同一种类型。在两种类型来回转换时要小心,因为 BigInt 变量在转换成 Number 变量时可能会丢失精度。

4️⃣ Symbol#

Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。

每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。

需要注意的是通过 Symbol 方法创建值的时候不用使用 new 操作符,原因是通过 new 实例化的结果是一个 object 对象,而不是原始类型的 symbol

应用场景#

  • 定义常量,消除与业务无关的字符
const tabTypes = {
basic: Symbol(),
super: Symbol(),
}
if (type === tabTypes.basic) {
return <div>basic tab</div>
}
if (type === tabTypes.super) {
return <div>super tab</div>
}
  • 作为对象属性,避免多属性的复杂对象中,容易覆盖属性名的问题。
const name = Symbol('name');
const obj = {
[name]: 'ClickPaas',
}
  • 模拟类的私有方法

ES6 中的类是没有 private 关键字来声明类的私有方法和私有变量的,但是可以利用 Symbol 的唯一性来模拟。

const speak = Symbol();
class Person {
[speak]() {
...
}
}

5️⃣ 如何判断数据类型?#

typeof#

typeof 只能判断出基本类型和引用类型,其中有两个需要注意的情况:

  • typeof NaN
typeof NaN; // number
Number.NaN; // NaN
NaN === NaN; // false

ECMAScript标准指出,数字应为IEEE-754浮点数据。这包括Infinity,-Infinity和NaN。 根据定义,NaN是具有不确定数值结果的运算的返回值。因此,在JavaScript中,NaN 除了成为全局对象的一部分之外,还成为 Number 对象的一部分:Number.NaNNaN 仍然是数字数据类型,但未定义为实数

  • typeof null
typeof null // "object"

JavaScript 类型值是存在32 bit 单元里,32位有1-3位表示TYPE TAG,其它位表示真实值。而表示object的标记位正好是低三位都是0。

  • 1:整型(int)

  • 000:引用类型(object)

  • 010:双精度浮点型(double)

  • 100:字符串(string)

  • 110:布尔型(boolean)

但有两个特殊值:

  • undefined,用整数−2^30(负2的30次方,不在整型的范围内)
  • null,机器码空指针(C/C++ 宏定义),低三位也是000

由于 null 代表的是空指针(低三位也是 000 ),因此,null 的类型标签是 000typeof null 也因此返回 "object"。

instanceof#

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

note

需要注意的是,如果表达式 obj instanceof Foo 返回 true,则并不意味着该表达式会永远返回 true,因为 Foo.prototype 属性的值有可能会改变,改变之后的值很有可能不存在于 obj 的原型链上,这时原表达式的值就会成为 false。另外一种情况下,原表达式的值也会改变,就是改变对象 obj 的原型链的情况,虽然在目前的ES规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 proto 伪属性,是可以实现的。比如执行 obj.proto = {} 之后,obj instanceof Foo 就会返回 false 了。

instanceof 的实现原理是:右侧构造函数的 prototype 属性在左侧实例对象的原型链上则返回 true,否则返回 false。因此, instanceof 在查找过程中会遍历左侧实例对象的原型链。简单实现如下:

function new_instance_of(obj, Cons) {
let consProto = Cons.prototype; // 取右侧构造函数的 prototype 值
obj = obj.__proto__; // 取左侧对象实例的__proto__值
while (true) {
if (obj === null) {
return false;
}
if (obj === consProto) {
return true;
}
obj = obj.__proto__;
}
}

Object.prototype.toString()#

var toString = Object.prototype.toString;
toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]