7种数据类型
Undefined、Null、Boolean、String、Number、Symbol、Object
基本类型的变量是存放在栈区的
引用类型的值是同时保存在栈内存和堆内存中的
Undefined
通常指变量未定义。
Null
typeof null // "object"
null == undefined // true
null 值表示一个空对象指针。
不同的对象在底层原理的存储是用二进制表示的,在 javaScript 中,如果二进制的前三位都为 0 的话,系统会判定为是 Object 类型。null 的存储二进制是 000 ,也是前三位,所以系统判定 null 为 Object 类型。
Boolean
Boolean(NaN); // false
String
方法:
- split:使用:
var str = "hello world";
var arr = str.split('');
Number
使用 IEEE 754 格式表示整数和浮点值。
进制表示:0b
—— 二进制0o
—— 八进制0x
或0X
—— 十六进制
通常来说,有前导 0 的数值会被视为八进制,但是如果前导 0 后面有数字 8 和 9 ,则该数值被视为十进制。
如果要将 0b 和 0o 前缀的字符串数值转为十进制,要使用Number
方法。Number('0b111') // 7
科学计数法
以下两种情况,JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。
- 小数点前的数字多于 21 位。
- 小数点后的零多于 5 个。
正零和负零
-0 === +0 // true
0 === -0 // true
0 === +0 // true
几乎所有场合,正零和负零都会被当作正常的 0 。
唯一有区别的场合是,+0
或 -0
当做分母,返回的值是不相等的。
(1 / +0) === (1 / -0) // false
上面的代码之所以出现这样的结果,是因为除以正零得到 +Infinity
,除以负零得到 -Infinity
,这两者是不相等的。
浮点值
- 存储浮点值使用的内存空间是存储数值的两倍,所以 ECMAScript 总是想方设法把值转换为整数。
- 默认情况下, ECMAScript 会将小数点后至少包含 6 个零的浮点值转换为科学计数法。
- 浮点值的精确度最高可达 17 位小数,但在算术计算中远不如整数精确。例如, 0.1 + 0.2 不等于 0.3 。(之所以存在这种舍入错误,是因为使用了 IEEE 754 数值,这种错误并非 ECMAScript 所独有。其他使用相同格式的语言也有这个问题)
**NaN**
NaN是 JavaScript 的特殊值,表示“非数字”,主要出现在将字符串解析成数字出错的场合。
NaN
不等于任何值,包括它本身。- 数组的
indexOf
方法内部使用的是严格相等运算符,所有该方法对NaN
不成立。 NaN
在布尔运算时被当作false
。Boolean(NaN) // false
NaN
与任何数(包括它自己)的运算,得到的都是NaN
。
返回 NaN
的情况:
- 零除以零;
- 无穷大除以无穷大
- 给任意负数作开方运算(
Math.pow(-1, 1/3)
) - 算数运算符与不是数字或无法转换为数字的操作数一起使用时
- isNaN() 函数。接收一个参数,可以是任意数据类型,然后判断这个参数是否 “不是数值”。把一个值传给 isNaN() 后,该函数会尝试把它转换为数值。某些非数值的值可以直接转换成数值。不然转换为数值的值会导致这个函数返回 true 。
**Infinity**
Infinity
表示“无穷”,用来表示两种场景。一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非 0 数值除以 0 ,得到 Infinity
。
// 场景一
Math.pow(2, 1024)
// Infinity
// 场景二
0 / 0 // NaN
1 / 0 // Infinity
Infinity
有正负之分,Infinity
表示正的无穷,-Infinity
表示负的无穷。Infinity
大于一切数值(除了 NaN
),-Infinity
小于一切数值(除了 NaN
)。
0 * Infinity // NaN
0 / Infinity // 0
Infinity / 0 // Infinity
Infinity + Infinity // Infinity
Infinity * Infinity // Infinity
Infinity / Infinity // NaN
Infinity - Infinity // NaN
Infinity
与null
计算时,null
会转成 0 ,等同于与 0 的计算。Infinity
与undefined
计算,返回的都是NaN
- 要确定一个值是不是有限大(即介于 JavaScript 能表示的最小值和最大值之间),可以使用
isFinite()
函数。
常数
- Number.EPSILON:表示1与大于1的最小浮点数之间的差,实际上是JavaScript能够表示的最小精度
- Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER:JavaScript能够准确表示的整数范围在 -2^53 到 2^53 之间(不含两个端点),超过这个范围,无法精确表示这个值。ES6 引入了
Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
这两个常量,用来表示这个范围的上下限。由于2的53次方是一个16位的十进制数值,所以简单的法则就是,JavaScript对15位的十进制数都可以精确处理。 - Number.MIN_VALUE,最小数值,在多数浏览器中是 5e-324
- Number.MAX_VALUE,最大数值,1.7976931348623157e+308。
- Number.NEGATIVE_INFINITY 和 Number.POSITIVE_INFINITY 分别表示 -Infinity 和 Infinity 。
判断是否是数字
```javascript function isNumber(num) { return typeof num === “number” && !isNaN(num); // typeof NaN === ‘number’ }
// 或者 function isNumber(num) { return typeof num === “number” && isFinite(num); }
// 或者 function isNumber(num) { return num === !num; }
<a name="GJrQc"></a>
### 取小数点后几位
```javascript
// 会四舍五入
let num = 1.3489;
num = num.toFixed(2); // 1.35
Symbol
Symbol
值通过 Symbol
函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol
类型。凡是属性名属于 Symbol
类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。如果参数是一个对象,就会调用该对象的 toString 方法,将其转为字符串,然后才生成一个 Symbol 值。
注意:
Symbol
函数前不能使用new
命令。Symbol
值作为对象属性名: 可以用方括号和Object.defineProperty
,不能用点运算符。
转换规则:
Symbol
值可以显式转为字符串var sym = Symbol('hello');
String(sym) // 'Symbol(hello)'
sym.toString() // 'Symbol(hello)'
Symbol
值转布尔值Boolean(sym) // true
!sym // false
if (sym) {}
-
Object
内置对象
Arguments、Array、Boolean、Date、Error、Function、Math、Number、Object、RegExp、String
Date
var myDate = new Date();
myDate.getYear(); //获取当前年份(2位)
myDate.getFullYear(); //获取完整的年份(4位,1970-????)
myDate.getMonth(); //获取当前月份(0-11,0代表1月) // 所以获取当前月份是myDate.getMonth()+1;
myDate.getDate(); //获取当前月份中的当前日(1-31)
myDate.getDay(); //获取当前星期X(0-6,0代表星期天)
myDate.getTime(); //获取当前时间(从1970.1.1开始的毫秒数,13位数字,精度毫秒)
myDate.getHours(); //获取当前小时数(0-23)
myDate.getMinutes(); //获取当前分钟数(0-59)
myDate.getSeconds(); //获取当前秒数(0-59)
myDate.getMilliseconds(); //获取当前毫秒数(0-999)
myDate.toLocaleDateString(); //获取当前日期
var mytime = myDate.toLocaleTimeString(); //获取当前时间
myDate.toLocaleString(); //获取日期与时间
精度为秒(10位数字)的时间戳:
Math.round(new Date().getTime() / 1000)
Math
Math.round() :四舍五入取整
- Math.ceil():向上取整
- Math.floor():向下取整
Object 的遍历
ES6 一共有 5 种方法可以遍历对象的属性:
for...in
(es3就存在):遍历对象所有可继承的可枚举属性,包括原型链上的可枚举属性(不含Symbol
属性)。无法保证遍历顺序,应尽量避免编写依赖对象属性顺序的代码。
注意:如果属性的 _enumerable_
设置为 _false_
,则无法被遍历到。如果想过滤掉原型链上的属性,可以用 hasOwnProperty 方法来进行过滤。
Object.keys(obj)
(es5新增):返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含**Symbol**
属性)的键名,不包含原型链上的属性。与for in + hasOwnProperty
的效果一样。Object.getOwnPropertyNames(obj)
(es5新增):返回一个数组,包括对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)的键名,不包含原型链上的属性。Object.getOwnPropertySymbols(obj)
:返回一个数组,包含对象自身的所有Symbol
属性的键名。Reflect.ownKeys(obj)
:返回一个数组,包含对象自身的所有键名,不管键名是Symbol
或字符串,也不管是否可枚举。
举个例子:
const parent = Object.create(Object.prototype, {
a: {
value: 1,
writable: true,
enumerable: true,
configurable: true
}
})
const child = Object.create(parent, {
b: {
value: 2,
writable: true,
enumerable: true,
configurable: true
},
c: {
value: 3,
writable: true,
enumerable: false,
configurable: true
}
})
// for in
for(let i in child) {
if (child.hasOwnProperty(i)) {
console.log(i); // b
}
}
// Object.keys
console.log(Object.keys(child)) // ['b']
// Object.getOwnPropertyNames
console.log(Object.getOwnPropertyNames(child)); // ['b', 'c']
const obj = {100: 'a', 2: 'b', 7: 'c' };
Object.values(obj); // ["b", "c", "a"] 不按顺序输出
属性遍历的次序规则:
- 首先遍历所有数值键,按照数值升序排列。
- 其次遍历所有字符串,按照加入时间升序排列。
- 最后遍历所有
Symbol
键,按照加入时间升序排排列。
Object的两类属性
数据属性(四个特征):
value
:就是属性的值writable
:决定属性能否被赋值,默认为 trueenumerable
:决定for in
能否枚举该属性。默认情况下,所有直接定义在对象上的属性的这个特性都是true
。configurable
:决定该属性能够被删除(delete)以及除 value 和 writable 特性外的其他特性是否可以被修改,默认为 true
看颜色可以知道谁是可枚举的。 ```javascript // configurable var obj1 = {}; Object.defineProperties(obj1, { name: { get() {var b = {};
Object.defineProperties(b, {
name: {
get() {
return "huangry"
},
configurable: false
}
});
Object.getOwnPropertyDescriptor(b, "name"); // {set: undefined, enumerable: false, configurable: false, get: ƒ}
Object.keys(b); // []
b.age = 18;
Object.getOwnPropertyDescriptor(b, "age"); // {value: 18, writable: true, enumerable: true, configurable: true}
Object.keys(b); // ["age"]
}, configurable: false } }); Object.defineProperty(obj1, “name”, { value: “hippo” }) // TypeError Object.defineProperty(obj1, “name”, { configurable: true }) // TypeError delete obj.name; // falsereturn "huangry"
var obj2 = {name: “huangry”}; Object.defineProperties(obj2, { name: { configurable: false } }); Object.defineProperty(obj2, “name”, { value: “hippo” }) // {name: “hippo”}
访问器属性(四个特征):
- `getter`:函数或 undefined ,在取属性值时被调用
- `setter`:函数或 undefined ,在设置属性值时被调用
- `enumerable`:决定 `for in` 能否枚举该属性
- `configurable`:决定该属性能够被删除或者改变特征值
可通过 `Object.getOwnPropertyDescripter` 查看属性的特征值<br />可通过 `Object.defineProperty` 来定义或改变属性的特征
**对象的分类**
- 宿主对象:例如 `window` 。
- 内置对象
- 固有对象
- 原生对象
- 普通对象
<a name="fejet"></a>
# Map
`Map` 是一种新的集合类型, `Map` 的大多数特性都可以通过 `Object` 类型实现,但二者之间还是存在一些细微的差异。
<a name="LWXwe"></a>
## 基本 API
使用 `new` 关键字和 `Map` 构造函数可以创建一个空映射:
```javascript
const m = new Map()
初始化之后,可以使用 set()
方法再添加键/值对。另外,可以使用 get()
和 has()
进行查询,可以通过 size
属性获取映射中的键/值对的数量,还可以使用 delete()
和 clear()
删除值。
与 Object
只能使用数值、字符串或符号作为键不同, Map
可以使用任何 JavaScript 数据类型作为键。
与 Object
类型的一个主要差异是, Map
实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作。
选择 Object 还是 Map
- 内存占用
Object
和Map
的工程级实现在不同浏览器间存在明显差异,但存储单个键/值对所占用的内存数量都会随键的数量线性增加。批量添加或删除键/值对则取决于各浏览器对该类型内存分配的工程实现。不同浏览器的情况不同,但给定固定大小的内存,Map
大约可以比Object
多存储50%的键/值对。 - 插入性能
向Object
和Map
中插入新键/值对的消耗大致相当,不过插入Map
在所有浏览器中一般会稍微快一点儿。对这两个类型来说,插入速度并不会随着键/值对数量而线性增加。如果代码涉及大量插入操作,那么显然Map
的性能更佳。 - 查找速度
与插入不同,从大型Object
和Map
中查找键/值对的性能差异极小,但如果只包含少量键/值对,则Object
有时候速度更快。在把Object
当成数组使用的情况下(比如使用连续整数作为属性),浏览器引擎可以进行优化,在内存中使用更高效的布局。这对Map
来说是不可能的。对这两个类型而言,查找速度不会随着键/值对数量增加而线性增加。如果代码涉及大量查找操作,那么某些情况下可能选择Object
更好一些。 - 删除性能
使用delete
删除Object
属性的性能一直以来饱受诟病,目前在很多浏览器中仍然如此。为此,出现了一些伪删除对象属性的操作,包括把属性值设置为undefined
或null
。但很多时候,这都是一种讨厌的或不适宜的折中。而对大多数浏览器引擎来说,Map
的delete()
操作都比插入和查找更快。如果代码涉及大量删除操作,那么毫无疑问应该选择Map
。WeakMap
ECMAScript 6新增的“弱映射”(WeakMap
)是一种新的集合类型,为这门语言带来了增强的键/值对存储机制。WeakMap
是Map
的“兄弟”类型,其API也是Map
的子集。WeakMap
中的“weak”(弱),描述的是JavaScript垃圾回收程序对待“弱映射”中键的方式。
基本 API
可以使用new
关键字实例化一个空的WeakMap
:
const wm = new WeakMap();
初始化之后可以使用set()再添加键/值对,可以使用get()和has()查询,还可以使用delete()删除。
Set
WeakSet
类型转换
“==”
规则:
NaN
不等于任何值,包括其本身- 布尔值会转成数字类型
- 数字和字符串比较,字符串会转换成数字
undefined
和null
除了和undefined
和null
相等,和其它相比都是false
ES5规范11.9.3抽象相等比较算法
- 若 Type(x) 与 Type(y) 相同,则
- 若 Type(x) 为
**Undefined**
,返回true
。 - 若 Type(x) 为
**Null**
,返回true
。 - 若 Type(x) 为
**Number**
,则- 若 x 为
NaN
,返回false
。 - 若 y 为
NaN
,返回false
。 - 若 x 与 y 为相等数值,返回
true
。 - 若 x 为
+0
且 y 为-0
,返回true
。 - 若 x 为
-0
且 y 为+0
,返回true
。 - 返回 false 。
- 若 x 为
- 若 Type(x) 为
**String**
,则当 x 和 y 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回true
。否则,返回false
。 - 若 Type(x) 为
**Boolean**
,当 x 和 y 同为true
或者同为false
时返回true
。否则,返回false
。 - 当 x 和 y 为引用同一对象时返回
true
。否则,返回false
。
- 若 Type(x) 为
- 若 x 为
null
且 y 为undefined
,返回true
。 - 若 x 为
undefined
且 y 为null
,返回true
。 - 若 Type(x) 为
Number
且 Type(y) 为String
,返回comparison x == ToNumber(y)
的结果。 - 若 Type(x) 为
String
且 Type(y) 为Number
,返回比较ToNumber(x) == y
的结果。 - 若 Type(x) 为
Boolean
,返回比较ToNumber(x) == y
的结果。 - 若 Type(y) 为
Boolean
,返回比较x == ToNumber(y)
的结果。 - 若 Type(x) 为
String
或Number
,且 Type(y) 为Object
,返回比较x == ToPrimitive(y)
的结果。 - 若 Type(x) 为
Object
且 Type(y) 为String
或Number
,返回比较ToPrimitive(x) == y
的结果。 - 返回false。
null == undefined // true
true == 1 //true
false == 0 // true
"123" == 123 //true
"1a" == 1 //false,"1a"转成数字是NaN
undefined == 0 // false
undefined == false // false
new String("a") == "a" // true
new String("a") == new String("a") // false
X → Number
数据类型 | 数字类型 |
---|---|
字符串 | 1) 数字转化为对应的数字 2) 其他转化为 NaN |
布尔类型 | 1) true 转化为 1 2) false 转化为 0 |
null | 0 |
undefined | NaN |
数组 | 1) 数组为空转化为 0; 2) 数组只有一个元素转化为对应元素; 3) 其他转化为NaN |
空字符串 | 0 |
Number(10); // 10
Number('10'); // 10
Number(null); // 0
Number(''); // 0
Number(true); // 1
Number(false); // 0
Number([]); // 0
Number([1,2]); // NaN
Number('10a'); // NaN
Number(undefined); // NaN
字符串转数字
parseInt(s); // 解析时会跳过任意数量的前导空格,尽可能解析更多数值字符
parseFloat(s);
Number(s); // 字符中不能出现非数字的字符,否则将返回 NaN
parseInt && parseFloat
paeseInt
方法用于将字符串转为整数。如果字符串头部有空格,空格会被自动去除。如果 parseInt
的参数不是字符串,则会先转为字符串再转换。字符串转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分。
如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回 NaN
。
所以, parseInt
的返回值只有两种可能,要么是一个十进制整数,要么是 NaN
。
如果字符串以 0x
或 0X
开头, parseInt
会将其按照十六进制数解析。
对于那些会自动转为科学计数法的数字, parseInt
会将科学计数法的表示方法视为字符串,因此导致一些奇怪的结果。
parseInt('1e+21') // 1
parseInt
方法还可以接受第二个参数(2到36之间),表示被解析的值的进制,返回该值对应的十进制数。默认情况下, parseInt
的第二个参数为 10 ,即默认是十进制转十进制。
parseInt('1000', 2) // 8
parseInt('1000', 6) // 216
如果第二个参数不是数值,会被自动转为一个整数。这个整数只有在 2 到 36 之间,才能得到有意义的结果,超出这个范围,则返回 NaN 。如果第二个参数是 0 、 undefined 和 null ,则直接被忽略。
如果字符串包含对于指定进制无意义的字符,则从最高位开始,只返回可以转换的数值。如果最高位无法转换,则直接返回 NaN 。
X → String
对于原始类型来说,转字符串类型会默认调用 toString() 方法。
数据类型 | String类型 |
---|---|
数字 | 转化为数字对应的字符串 |
true | 转化为字符串 “true” |
null | 转化为字符串 “null” |
undefined | 转化为字符串 “undefined” |
Object | 转化为 “[object Object]” |
x → Boolean
数据类型 | 转化为 true 的值 | 转换为 false 的值 |
---|---|---|
Boolean | true | false |
String | 非空字符串 | 空字符串 |
Number | 非零数值(包括无穷值) | 0 、 NaN |
Object | 任意对象 | null |
Undefined | N/A(不存在) | undefined |
Boolean("") //false
Boolean(undefined) // false
Boolean(null) // false
Boolean(NaN) // false
Boolean(false) // false
Boolean(0) // false
Boolean({}) // true
Boolean([]) // true
判断数据类型
typeof
返回的值有:
"undefined"
"boolean"
"string"
"number"
"object" 表示值为对象(而不是函数)或 null
"function"
"symbol"
除了 null 类型以及 Object 类型不能准确判断外,其他数据类型都可能返回正确的类型。
既然 typeof 对对象类型都返回 Object 类型情况的局限性,我们可以使用 instanceof 来进行判断某个对象是不是另一个对象的实例。返回值的是一个布尔类型。
typeof undefined // 'undefined'
typeof null // 'object'
typeof Symbol() // 'symbol'
typeof Function // 'function'
typeof [] // 'object'
typeof {} // 'object'
instanceof
Object.prototype.toString.call()
最好的
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('hi') // "[object String]"
Object.prototype.toString.call({a:'hi'}) // "[object Object]"
Object.prototype.toString.call([1,'a']) // "[object Array]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(() => {}) // "[object Function]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
参考链接: