JavaScrit中的数据类型

JavaScript中有七种数据类型,本文主要描述了我在阅读JavaScript教程时我不会的细节知识点。

Number

存储方式

所有的number都是以64位浮点数形式存储的,所以当计算的时候结果并不准确。

1
2
0.1 + 0.2 // 0.30000000000000004
0.3 / 0.1 // 2.9999999999999996

当进行位运算时,会转换为32位的整型

1
~1.1 // -2

精度

根据国际标准 IEEE 754,JavaScript 浮点数的64个二进制位,从最左边开始,是这样组成的。

  • 第1位:符号位,0表示正数,1表示负数
  • 第2位到第12位(共11位):指数部分
  • 第13位到第64位(共52位):小数部分(即有效数字)

计算公式:

1
(-1)^符号位 * 小数部分 * 2^指数部分

其中小数部分表示1.xxxx…x,所以只需存储xxxx…x部分即可。

1
2
3
// 当整数值大于2^53时计算会出现错误
2 ** 53 // 9007199254740992
2 ** 53 + 10 //9007199254741002

因为64位浮点数最多可以表示53个二进制位,所以在JavaScript中绝对值小于2^53^的整数(-9007199254740992~9007199254740992)可以被精确表示,也可以简记为15位的十进制整数可以精确表示。

1
2
3
4
// 当浮点数大于可表示的最大值时,会上溢出,转换为Infinity
2 ** 1024 // Infinity
// 当浮点数小于可表示的最小值时,会下溢出,转换为0
2 ** -1024 // 0

因为指数部分的值为11位,可以表示的范围为[-1023, 1023],所以JavaScript中浮点数可以表示的值范围为[2^-1023^, 2^1024^)

JavaScript 提供Number对象的MAX_VALUEMIN_VALUE属性,返回可以表示的具体的最大值和最小值。

1
2
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324

数值表示方法

科学计数法表示

1
123e3 // => 123 * (10 ** 3) => 123000

当整数部分多余21位时,会使用科学计数法表示

1
1234567890123456789012 // 1.2345678901234568e+21

小数点后的零多于5个,同样会使用科学计数法表示

1
0.0000003 // => 3 * (10 ** -7) => 3e-7

不同进制表示

1
2
3
4
5
6
7
8
9
10
0xff // 16进制 => 255
0o377 // 8进制 => 255
0b11 // 2进制 => 3
// 如果第二位没有字母标识且没有大7的数字则当做8进制解释
011 // 8进制 => 9
// 如果有8、9则按十进制解释
088 // => 88
// 其余不符合格式的情况会报错
0f // SyntaxError: Invalid or unexpected token
0b2 // SyntaxError: Invalid or unexpected token

其余问题详见阮一峰JavaScript教程

1
2
3
4
5
6
7
8
NaN === NaN // false
0 / 0 // NaN
1 / 0 // Infinity
1 / -0 // -Infinity
Infinity > NaN // false
-Infinity > NaN // false
Infinity < NaN // false
-Infinity < NaN // false

String

JavaScript 对 UTF-16 的支持是不完整的,由于历史原因,只支持两字节的字符,不支持四字节的字符。这是因为 JavaScript 第一版发布的时候,Unicode 的码点只编到U+FFFF,因此两字节足够表示了。后来,Unicode 纳入的字符越来越多,出现了四字节的编码。但是,JavaScript 的标准此时已经定型了,统一将字符长度限制在两字节,导致无法识别四字节的字符。上一节的那个四字节字符𝌆,浏览器会正确识别这是一个字符,但是 JavaScript 无法识别,会认为这是两个字符。

JavaScript 原生提供两个 Base64 相关的方法。

  • btoa():任意值转为 Base64 编码
  • atob():Base64 编码转为原来的值
1
2
3
var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"

注意,这两个方法不适合非 ASCII 码的字符,会报错。

1
btoa('你好') // 报错

Boolean

1
2
3
4
5
6
7
8
// 只有这7种情况为false,其余情况全都为true
Boolean(undefined) // false
Boolean(null) // false
Boolean(false) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean("") // false
Boolean('') // false

null

表示无

1
2
3
4
// 遵循c语言的传统,null转换为number为0
Number(null) // 0
// 由于历史原因,最开始参考Java将null当做对象,因为需要兼容早期版本,所以null为特殊的object
typeof null // 'object'

undefined

表示未定义

1
2
// undefined转换为number为NaN
Number(undefined) // NaN

Object

for...in循环有两个使用注意点。

  • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
  • 它不仅遍历对象自身的属性,还遍历继承的属性。

Function

函数的name属性返回函数的名字。

1
2
function f1() {}
f1.name // "f1"

如果是通过变量赋值定义的函数,那么name属性返回变量名。

1
2
var f2 = function () {};
f2.name // "f2"

但是,上面这种情况,只有在变量的值是一个匿名函数时才是如此。如果变量的值是一个具名函数,那么name属性返回function关键字之后的那个函数名。

1
2
var f3 = function myName() {};
f3.name // 'myName'

上面代码中,f3.name返回函数表达式的名字。注意,真正的函数名还是f3,而myName这个名字只在函数体内部可用。

name属性的一个用处,就是获取参数函数的名字。

1
2
3
4
5
6
7
var myFunc = function () {};

function test(f) {
console.log(f.name);
}

test(myFunc) // myFunc

上面代码中,函数test内部通过name属性,就可以知道传入的参数是什么函数

array

JavaScript 使用一个32位整数,保存数组的元素个数。这意味着,数组成员最多只有 4294967295 个(232 - 1)个,也就是说length属性的最大值就是 4294967295。

如果数组的键名是添加超出范围的数值,该键名会自动转为字符串。

1
2
3
4
5
6
7
var arr = [];
arr[-1] = 'a';
arr[2 ** 32)] = 'b';

arr.length // 0
arr[-1] // "a"
arr[4294967296] // "b"

Symbol(es6)

symbol是es6的新数据类型,简而言之就是一个独一无二的可以作为key的类型。暂时不做过多的了解,详细的教程看ECMAScript入门或者MDN

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。


本文链接:JavaScrit中的数据类型
版权声明:本文章采用CC BY-NC-SA 3.0 CN许可协议进行许可。转载请注明出处!