JavaScript数据类型
ECMAScript 有 7 种简单数据类型(也称为原始类型):Undefined、Null、Boolean、Number、 String、 BigInt 和 Symbol。Symbol 和 BigInt 是 ECMAScript 6+ 新增的。还有 1 种复杂数据类型叫 Object(对 象)。Object 是一种无序名值对的集合。
数据分成两大类的本质区别:
简单数据类型 和 引用数据类型 它们在内存中的存储方式不同。简单数据类型是直接存储在栈中的简单数据段,占据空间小,属于被频繁使用的数据。引用数据类型是存储在堆内存中,占据空间大。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
判断变量类型的方法
JavaScript有4种方法判断变量的类型,分别是 typeof 、 instanceof 、Object.prototype.toString.call()(对象原型链判断方法) 、 constructor (用于引用数据类型)。
typeof:常用于判断基本数据类型,对于引用数据类型除了function返回'function',其余全部返回'object'。instanceof:主要用于区分引用数据类型,检测方法是检测的类型在当前实例的原型链上,用其检测出来的结果都是true,不太适合用于简单数据类型的检测,检测过程繁琐且对于简单数据类型中的undefined, null, symbol检测不出来。constructor:用于检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰。Object.prototype.toString.call():适用于所有类型的判断检测,检测方法是Object.prototype.toString.call(数据) 返回的是该数据类型的字符串。
这四种判断数据类型的方法中,各种数据类型都能检测且检测精准的就是 Object.prototype.toString.call() 这种方法。
instanceof 的实现原理:验证当前类的原型prototype是否会出现在实例的原型链__proto__上,只要在它的原型链上,则结果都为true。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,找到返回true,未找到返回false。
Object.prototype.toString.call() 原理:Object.prototype.toString 表示一个返回对象类型的字符串,call()方法可以改变this的指向,那么把Object.prototype.toString()方法指向不同的数据类型上面,返回不同的结果。
Undefined 类型
undefind 是全局对象的一个属性,当一个变量没有被赋值或者一个函数没有返回值或者某个对象不存在某个属性却去访问或者函数定义了形参但没有传递实参,这时候都是undefined。undefined通过typeof判断类型是'undefined'。
let message; // 这个变量被声明了,只是值为 undefined
// 确保没有声明过这个变量
// let age
console.log(typeof message); // "undefined"
console.log(typeof age); // "undefined"
无论是声明还是未声明,typeof 返回的都是字符串"undefined"。
Null 类型
Null 类型同样只有一个值,即特殊值 null。逻辑上讲,null 值表示一个空对象指针,这也是给 typeof 传一个 null 会返回"object"的原因。
let test = null;
console.log(typeof test); // "object"
null 其实属于自己的类型 Null,而不属于Object类型,typeof 之所以会判定为 Object 类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。 对象被赋值了null 以后,对象对应的堆内存中的值就是游离状态了,GC 会择机回收该值并释放内存。因此,需要释放某个对象,就将变量设置为 null,即表示该对象已经被清空,目前无效状态。
null与undefined
undefined == undefined
undefined === undefined
null === null
null == null
null == undefined
null !== undefined
// 均为true
Boolean 类型
Boolean(布尔值)类型是 ECMAScript 中使用最频繁的类型之一,有两个字面值:true 和 false。
不同类型与布尔值之间的转换规则。
| 数据类型 | 转换为true的值 | 转换为false的值 |
|---|---|---|
| Boolean | true | false |
| String | 非空字符串 | ""(空字符串) |
| Number | 非零数值(包括无穷值) | 0、NaN |
| Object | 任意对象 | null |
| Undefined | N/A(不存在) | undefined |
Number 类型
分为十进制、二进制、八进制和十六进制。
二进制表示为开头是 0b,八进制表示为开头是 0o,十六进制表示为开头是 0x,大小写均可。
由于内存的限制,ECMAScript 并不支持表示这个世界上的所有数值。ECMAScript 可以表示的最小数值保存在 Number.MIN_VALUE 中,这个值在多数浏览器中是 5e-324;可以表示的最大数值保存在 Number.MAX_VALUE 中,这个值在多数浏览器中是 1.7976931348623157e+308。
数值转换: 有 3 个函数可以将非数值转换为数值:Number()、parseInt()和 parseFloat()。Number()是转型函数,可用于任何数据类型。后两个函数主要用于将字符串转换为数值。
String 类型
字符串可以使用双引号(")、 单引号(')或反引号(`)标示:
let firstName = "John";
let lastName = 'Jacob';
let lastName = `Jingleheimerschmidt`;
转换为字符串: 1.toString()方法,这个方法唯一的用途就是返回当前值的字符串等价物。2.用加号操作符给一个值加上一个空字符串""也可以将其转换为字符串。
BigInt 类型
BigInt 是一种内置对象,它提供了一种方法来表示大于 2^53 - 1 的整数。这原本是 Javascript 中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数。
可以用在一个整数字面量后面加 n 的方式定义一个 BigInt,或者调用函数 BigInt()并传递一个整数值或字符串值:
const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
// ↪ 9007199254740991n
const hugeString = BigInt("9007199254740991");
// ↪ 9007199254740991n
const hugeHex = BigInt("0x1fffffffffffff");
// ↪ 9007199254740991n
const hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111");
// ↪ 9007199254740991n
Symbol 类型
Symbol 是原始值,且符号实例是唯一、不可变的。 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');
console.log(genericSymbol == otherGenericSymbol); // false
console.log(fooSymbol == otherFooSymbol); // false
Symbol() 函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的 symbol 注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。
Object 类型
在 JavaScript 中,几乎所有的对象都是 Object 的实例;一个典型的对象从 Object.prototype 继承属性(包括方法),尽管这些属性可能被覆盖(或者说重写)。唯一不从 Object.prototype 继承的对象是那些 null 原型对象,或者是从其他 null 原型对象继承而来的对象。
对象原型属性
应该避免调用任何 Object.prototype 方法,特别是那些不打算多态化的方法(即只有其初始行为是合理的,且无法被任何继承的对象以合理的方式重写)。所有从 Object.prototype 继承的对象都可以自定义一个具有相同名称但语义可能与你的预期完全不同的自有属性。此外,这些属性不会被 null 原型对象继承。现代 JavaScript 中用于操作对象的工具方法都是静态的。
valueOf()、toString()和toLocaleString()存在的目的是为了多态化,你应该期望对象会定义自己的实现并具有合理的行为,因此你可以将它们作为实例方法调用。但是,valueOf()和toString()通常是通过强制类型转换隐式调用的,因此不需要在代码中自己调用它们。__defineGetter__()、__defineSetter__()、__lookupGetter__()和__lookupSetter__()已被弃用,不应该再使用。使用静态方法Object.defineProperty()和Object.getOwnPropertyDescriptor()作为替代。__proto__属性已被弃用,不应该再使用。使用静态方法Object.getPrototypeOf()和Object.setPrototypeOf()作为替代。propertyIsEnumerable()和hasOwnProperty()方法可以分别用静态方法Object.getOwnPropertyDescriptor()和Object.hasOwn()替换。- 如果正在检查一个构造函数的
prototype属性,通常可以用instanceof代替isPrototypeOf()方法。