【JavaScript 核心】数据类型

数据类型

前言

最新的 ECMAScript 标准定义了 8 种数据类型:

  • 7 种基本数据类型:
    • Undefined
    • Null
    • Boolean
    • Number
    • BigInt(ECMAScript 2020)
    • String
    • Symbol(ECMAScript 2015)
  • 1 种复杂数据类型(又称引用数据类型):
    • Object

基本数据类型保存在栈内存,引用类型保存在堆内存中。根本原因在于保存在栈内存的必须是大小固定的数据,引用类型的大小不固定,只能保存在堆内存中,但是可以把它的地址写在栈内存中以供我们访问。

如果是基本数据类型,则按值访问,操作的就是变量保存的值;如果是引用类型的值,我们只是通过保存在变量中的引用类型的地址来操作实际对象。

使用 typeof 操作符判断数据类型

typeof 用于检测给定变量的数据类型,对一个值使用 typeof 操作符会返回一个表示操作数的类型的字符串。但 typeof 的运算结果,与运行时类型的规定有很多不一致的地方。

我们可以看下表来对照一下。

示例表达式typeof 结果运行时类型行为
void(0)undefinedUndefined
nullobjectNull
truebooleanBoolean
7numberNumber
9007199254740992nbigintBigInt
“飞翔得牛二”stringString
Symbol(“牛二”)symbolSymbol
(function(){})functionFunction object
{}objectAny other object

在表格中,多数项是对应的,但是请注意 object —— Nullfunction —— Object 是特例,我们理解类型的时候需要特别注意这个区别。

此外,由于 typeof 是一个操作符而不是函数,后面可加括号也可省略。

8 种数据类型详细介绍

Undefined 类型

Undefined 类型只有一个值,即特殊的 undefined

Undefined值会被赋值给所有未初始化变量,在查看不存在的对象属性时也返回这个值。在布尔上下文中,未定义值被认为是假值。

注意: undefined 被认为是真正的基本数据类型。 除非显式转换,否则与在逻辑上下文中评估为false的其他类型相比,未定义的值可能会出现不可预料的行为。

var test;                         // 变量被声明但未定义,其值被赋为undefined值

var testObj = {};
console.log(test);                // test的值存在,显示为undefined
console.log(testObj.myProp);      // testObj存在,但属性不存在,显示为undefined
console.log(undefined == null);   // 未强制类型检查,显示为true
console.log(undefined === null);  // 强制类型检查,显示为false

注意:未定义没有内置的语言文字。因此(x === undefined)并不是检查变量是否未定义的万无一失的方法,因为在ECMAScript 5之前的版本,如果写代码var undefined = "I'm defined now";是合法的。更稳健的比较方法是(typeof x === 'undefined')。(—— 出自维基百科)

下属函数不会如期望那样工作:

function isUndefined(x) { var u; return x === u; }             // 如这个...
function isUndefined(x) { return x === void 0; }               // ... 或第二个
function isUndefined(x) { return (typeof x) === "undefined"; } // ... 或第三个

如果my_var是未知标识符,调用isUndefined(my_var)会抛出ReferenceError,但typeof my_var === 'undefined'不会抛出异常。

对未初始化和未声明的变量执行 typeof 操作符都会返回 undefined 值。

显示地初始化变量是明智的选择,这样当 typeof 操作符返回 "undefined" 值时,我们就知道被检测地变量还没有被声明,而不是尚未初始化。(—— 出自红宝书)

Null 类型

Null 类型也只有一个值,即特殊的 null
从逻辑角度来看,null 值表示一个空对象指针,所以使用 typeof 操作符检测 null 值时会返回 "object"

如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为 null 而不是其他值。这样一来,只要直接检查相应的变量是否等于 null 值就可以知道它是否已经保存了一个对象的引用。(—— 出自红宝书)

实际上,undefined 值是派生自 null 值的,因此 null == undefined 会返回 true,但 null === undefined 则返回 false 了。

Boolean 类型

Boolean 类型只有两个字面值:truefalse

Number 类型

Number 类型使用 IEEE 754 格式来表示浮点双精度数。不能精确表示一些实数或分数。如:

console.log(0.2 + 0.1 === 0.3); // false
console.log(0.94 - 0.01);       // 0.9299999999999999
1)浮点数值的整数化

因为保存浮点数值需要得内存空间是保存整数值的两倍,所以凡是可以「整数化」的浮点数都会被转换为整数值,例如:1.1.0 都会被解析为 1

对于那些极大或极小的数值,可以用 e 表示法(即科学计数法)表示的浮点数值表示。(用 e 表示法表示的数值等于 e 前面的数值乘以 10 的指数次幂)

2)数值范围限制

JavaScript 能够表示的最小数值Number.MIN_VALUE,在大多数浏览器中这个值是 5e-324
JavaScript 能够表示的最大数值Number.MAX_VALUE,在大多数浏览器中这个值是 1.7976931348623157e+308

超出范围的正数会被转换成 Infinity(正无穷),超出范围的负数会被转换成 -Infinity(负无穷)。

可以使用 isFinite() 函数判断括号里的参数是否位于最小与最大数值之间。

3)特殊的 NaN

NaN,即非数值(Not a Number)是一个特殊的数值。它有两个特点:一是任何涉及 NaN 的操作都会返回 NaN,二是 NaN 与任何值都不相等,包括 NaN 本身。

Infinity 和 NaN 是数字:

typeof Infinity;   // returns "number"
typeof NaN;        // returns "number"

NaN 不等于其自身:

const nan = NaN;
console.log(NaN == NaN);        // false
console.log(NaN === NaN);       // false
console.log(NaN !== NaN);       // true
console.log(nan !== nan);       // true

// 可以通过 `isNaN()` 函数来确认括号里的参数是否「不是数值」
console.log(isNaN("converted to NaN"));     // true
console.log(isNaN(NaN));                    // true
console.log(Number.isNaN("not converted")); // false
console.log(Number.isNaN(NaN));             // true

可以通过 isNaN() 函数来确认括号里的参数是否「不是数值」,需要注意的是,isNaN() 在接收到一个参数后,会尝试将这个值转换为数值,某些不是数值的值会直接转换为数值,例如字符串 "10"Boolean 值。

4)数值转换函数

有 3 个函数可以把非数值转换为数值:Number()parseInt()parseFloat()

由于 Number() 函数在转换字符串时比较复杂而且不够合理,因此更常用过的是另外两个函数。(—— 出自红宝书)

parseInt() 在转换时可以拥有第二个参数:转换时使用的基数(即多少进制),建议无论在什么情况下都明确指定基数。(—— 出自红宝书)

parseFloat() 只解析十进制值,因此它没有用第二个参数指定基数的用法。另外如果字符串没有小数点,或者小数点后都是零,parseFloat() 会返回整数。

转换规则:这 3 个函数都会忽略字符串前面的空格,直至找到第一个非空格字符。如果第一个字符不是数字字符或负号,就会返回 NaN,直到解析完所有后续字符或者遇到了一个非数字字符。
区别是 parseInt() 转换过程中,小数点不是有效的数字字符;而 parseFloat() 转换过程中,第一个小数点是有效的,后面的小数点是无效的,从第二个小数点开始的后面所有字符会被忽略。

BigInt 类型(ECMAScript 2020)

BigInt 类型是在 ECMAScript 2020(ES11)引入的新特性。

JavaScript 中能够精确表达的最大数字是 2⁵³ - 1,即 Number.MAX_SAFE_INTEGER,如果超过了这个范围,运算结果就不再准确了。

const max = Number.MAX_SAFE_INTEGER;
console.log(max); // 9007199254740991

console.log(max + 1); // 9007199254740992
console.log(max + 2); // 9007199254740992
console.log(max + 3); // 9007199254740994
console.log(Math.pow(2, 53) === Math.pow(2, 53) + 1); // true

而新的 BigInt 数据类型可以解决这个问题,它能够创建更大的数字。

通过在数字末尾加上字母 n,就可以将它转换成 BigInt。但要注意,我们无法将标准数字与 BigInt 数字混合在一起计算,否则将抛出 TypeError。

const bigNum = 100000000000000000000000000000n;
console.log(bigNum + 1n); // 200000000000000000000000000000n
console.log(bigNum + 1); // TypeError: Cannot mix BigInt and other types, use explicit conversions

String 类型

JavaScript中的字符串是一个字符序列。在JavaScript中,可以通过将一系列字符放在双引号(") 或单引号(')之间直接创建字符串(作为字面量)。此种字符串必须写在单行上,但可包含转义的换行符(如\n). JavaScript标准允许反引号字符(`,即重音符或反撇号)引用多行文字字符串以及模板文字,这允许在字符串内插入类型强制计算的表达式,但这仅在2016年开始的某些浏览器上支持:Firefox和Chrome,但Internet Explorer 11不支持。

var greeting = "Hello, World!";
var anotherGreeting = 'Greetings, people of Earth.';

可以使用charAt方法(由String.prototype提供)访问字符串中的单个字符。这是访问字符串中的单个字符时的首选方式,因为它也适用于非现代浏览器:

var h = greeting.charAt(0);

在现代浏览器中,可以通过与数组相同的表示法访问字符串中的单个字符:

var h = greeting[0];

但是,JavaScript字符串是不可变的

greeting[0] = "H"; // Fails.

如果字符串具有相同的内容,则将相等运算符 (“==”) 应用于两个字符串将返回 true,这意味着:具有相同的长度并包含相同的字符序列(大小写对字母很重要)。因此:

var x = "World";
var compare1 = ("Hello, " +x == "Hello, World"); // true.
var compare2 = ("Hello, " +x == "hello, World"); // false. 第二个字符串 h 大小写不一致

除非转义,否则不能嵌套相同类型的引号:

var x = '"Hello, World!" he said.'; // Just fine.
var x = ""Hello, World!" he said."; //  Not good.
var x = "\"Hello, World!\" he said."; // Works by escaping " with \"

String构造函数创建一个字符串对象(一个包装字符串的对象):

var greeting = new String("Hello, World!");

这些对象有一个valueOf方法,返回包装在其中的原始字符串:

var s = new String("Hello !");
typeof s; // Is 'object'.
typeof s.valueOf(); // Is 'string'.

两个String对象之间的相等与字符串原语不同:

var s1 = new String("Hello !");
var s2 = new String("Hello !");
s1 == s2; // Is false, because they are two distinct objects.
s1.valueOf() == s2.valueOf(); // Is true.

数值转换字符串

要把一个值转换为一个字符串有两种方式:

第一种,几乎每个值都有的 toString() 方法(除了 nullundefined)。其中数值型字符串在调用该方法时,可以传递一个参数——输出数值的基数(默认是十进制)。

第二种,String() 函数,它在转换过程中,如果值有 toString() 方法,则调用该方法(没有参数);如果值是 null,则返回 "null";如果值是 undefined,则返回 "undefined"

Symbol 类型(ECMAScript 2015)

Symbol 类型是在 ECMAScript 2015(ES6)引入的新特性。

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。因此 ES6 引入了一种新的基本数据类型 Symbol,表示独一无二的值。

例如:

var x = Symbol(1);
var y = Symbol(1);
console.log(x === y); // => false

var symbolObject = {};
var normalObject = {};

// since x and y are unique,
// they can be used as unique keys in an object
symbolObject[x] = 1;
symbolObject[y] = 2;

console.log(symbolObject[x]); // => 1
console.log(symbolObject[y]); // => 2

// as compared to normal numeric keys
normalObject[1] = 1;
normalObject[1] = 2; // overrides the value of 1

console.log(normalObject[1]); // => 2

// changing the value of x does not change the key stored in the object
x = Symbol(3);
console.log(symbolObject[x]); // => undefined

// changing x back just creates another unique Symbol
x = Symbol(1);
console.log(symbolObject[x]); // => undefined

关于 Symbol 的知识点可以参考阮一峰老师编写的《ES6标准入门(第3版)》中 Symbol 章节。

Object 类型

JavaScript 中的对象是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要创建的对象类型的名称来创建。

简单说,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。

判断数据类型

JavaScript 中判断数据类型主要有下列几种方式:

typeof

typeof 只能区分基本类型:undefined、object、boolean、number、bigint,string,symbol,function,object,对于 null、array、object 来说,使用 typeof 都会统一返回 object 字符串。

typeof {} // "object"
typeof [] // "object"
typeof null // "object"

Object.prototype.toString.call()

Object.prototype.toString.call() 能用于判断原生引用类型数据,返回一个形如 "[object XXX]" 的字符串。

判断基本类型:

Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call('abc'); // "[object String]"
Object.prototype.toString.call(123); // "[object Number]"
Object.prototype.toString.call(true); // "[object Boolean]"

判断原生引用类型:

// 函数类型
function fn(){
  console.log('test');
}
Object.prototype.toString.call(fn); // "[object Function]"

// 日期类型
var date = new Date();
Object.prototype.toString.call(date); // "[object Date]"

// 数组类型
var arr = [1,2,3];
Object.prototype.toString.call(arr); // "[object Array]"

// 正则表达式
var reg = /[hbc]at/gi;
Object.prototype.toString.call(reg); // "[object RegExp]"

但是无法判断自定义类型:

function Person(name, age) {
  this.name = name;
  this.age = age;
}
var person = new Person("Rose", 18);
Object.prototype.toString.call(arr); // "[object Object]"

很明显这种方法不能准确判断 personPerson 类的实例。

instanceof

instanceof 运算符用于测试构造函数的 prototype 属性是否出现在对象的原型链中的任何位置,

可以用来判断某个构造函数的 prototype 属性是否存在另外一个要检测对象的原型链上,即判断一个对象是否是某个构造函数或其子构造函数的实例。

它的用法类似于 object instanceof class

注意左侧必须是对象(object),如果不是,直接返回 false。

function Person(name, age) {
  this.name = name;
  this.age = age;
}
var person = new Person("Rose", 18);
console.log(person instanceof Person); // true

数据类型转换

参考 JavaScript 类型转换

参考资料

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值