一、typeof操作符
ECMAScript有6种简单数据类型(原始类型):
Undefined、Null 、Boolean、Number、String、Symbol(ES6新增)
一种复杂数据类型:
Object
由于ECMAScript的类型系统是松散型的,所以要一种手段来确定任意变量的数据类型。
typeof就是为此而生的,对于一个值使用typeof操作符回会返回以下字符串之一:
| 类型 | typeof后对应的值 |
|---|---|
| 未定义/定义了未初始化 | "undefined" |
| 布尔值 | "boolean" |
| 字符串 | "string" |
| 数值 | "number" |
| 对象/null | "object" |
| 函数 | "function" |
| 符号 | "symbol" |
需要特别注意的是:
调用typeof null 返回的是"object" 这是因为特殊值null 被认为是一个对空对象的引用
二、数据类型
2.1、Undefined
当使用var 或let声明了变量但是没有初始化时,就相当于赋予了undefined
-
对于声明但未初始化的变量以及未声明的变量使用typeof 都会返回undefined
- 所以,即使未初始化的变量会被赋予undefined,但我们也要在声明变量的时候同时进行初始化,这样,当typeof返回undeifned的时候会知道是因为给定的变量未声明导致的,而不是未初始化导致的
-
undefined是一个假值
let message; // 等同于 let message = undefined;
console.log(message == undefined); // true
console.log(message === undefined); //true
console.log(typeof message); // undefined (message声明了但未初始化)
console.log(typeof aaa); // undefined (aaa未声明)
if (!message) {
// undefined是一个假值
console.log('message未初始化');
}
2.2、Null
逻辑上讲,null值表示一个空对象指针,这也是typeof null为何返回"object"的原因
- 在定义将来要保存对象值的变量时,建议用null来初始化,这样就可以通过变量的值是不是null来判断是否赋予了一个对象的引用
- undefined是由null派生而来的,因此ECMA-262将它们定义为表面上相等:
即使null与undefined有关系,但是它们的用途不一样:永远不必显式的将变量的值设为undefined
但是null不是任何时候只要变量要保存的是对象而又没有对象可保存时,就要用null来填充
这样就可以保持null是空对象指针的语义,进一步与undefined区分开
- null是一个假值
console.log(null == undefined); // true 表面上相等
// 一个小技巧,当判断一个变量是null或undefined的时候,可以用"=="来快速判断:
// if (xx == undefined),相当于if(xx === null || xx === undefined)
2.3、Boolean
Boolean类型有两个字面量值: true、false
-
给变量赋值为布尔值的例子:
let found = true;
let lost = false;
(true和false是区分大小写的,其他写法都不是布尔值)
- 可以使用Boolean()转型函数将其它类型的值转为布尔值:(可以在任意类型的数据上使用)
| 数据类型 | 转为true的值 | 转为false的值 |
|---|---|---|
| Boolean | true | false |
| String | 非空字符串 | ""(空字符串) |
| Number | 非0数值(包括无穷值) | 0、NaN |
| Object | 任意对象 | null |
| Undefined | N/A(不存在) | undefined |
console.log(Boolean(NaN)); //false
console.log(Boolean(undefined)); // false
console.log(Boolean(null)); // false
2.4、Number
用以表示整数和浮点数
2.4.1、浮点值
要定义浮点值,数值中必须包含小数点且小数点后至少有一个数字
-
因为存储浮点值使用的内存空间是存储整数的两倍,所以ECMAScript总是想方设法把浮点转为整数:
- 在小数点后没有数字的情况下,数值就会变成整数
- 如果数值本身就是整数,只是小数点后跟着0,也会变成整数
let float1 = 1.; // 小数点后没有数字,当成整数1来处理
let float2 = 10.0; // 小数点后是0,当成整数10来处理
- 默认情况下,ECMAScript会将小数点后至少包含6个0的浮点数转为科学计数法
如:0.0000003会被转为3e-7
- 浮点值的精确度可高达17位,但是在算术计算中,远不如整数精确:
例如0.1+0.2 =0.30000000000000004 由于这种微小的舍入。导致很难测试特定的浮点值
(之所以会存在这种舍入错误,是因为使用了IEEE754数值)
if(a+b == 0.3) {
...
}
(千万不能这样,如果两个数是0.05 0.25 没问题,但是如果是0.1 0.2就有问题了)
// 解决0.1+0.2(将小数转换为整数之后,进行计算,然后再转换回对应的小数)
leta=0.1;
letb=0.2;
console.log((0.1*10+0.2*10) /10) ; // 0.3
2.4.2、值的范围
- 最小的数值保存在:Number.MIN_VALUE中(在多数浏览器中是5e-324)
- 最大的数值保存在:Number.MAX_VALUE中(在多数浏览器中是1.7976931348623157e+308)
-
如果某个计算得到的数值超过了JS可以表示的范围,那么会被自动转为一个特殊的:Infinity(无穷值)
-
任何无法表示的正数以 Infinity(正无穷大)表示
-
任何无法表示的负数以 -Infinity(负无穷大)表示
-
如果计算返回了Infinity / -Infinity 则该值不能进一步用于任何计算
-
判断一个值是不是有限大,可以使用isFinite() 函数
-
使用Number.NEGATIVE_INFINITY和Number.POSITIVE_INFINITY 可以获取Infinity和 -Infinity
-
let result = Number.MAX_VALUE + Number.MAX_VALUE;
console.log(isFinite(result)); // false
2.4.3、NaN
是一个特殊的数值,用于表示本来要返回数值的操作失败了:
比如0除任意数值在其它语言中通常会导致错误,从而终止代码运行
- 在ECMAScript中 0 +0 -0相除会返回NaN
- 如果分子是非0值,分母是有符号0或无符号0,则会返回Infinity / -Infinity
- 任何涉及到NaN的操作始终返回NaN
- NaN不等于包含NaN在内的任何值
-
可以使用isNaN(任意数据类型) 来判断这个参数是否“不是数值”
-
首先该函数会尝试把这个参数转为数值
-
任何不能转为数值的值都会导致这个函数返回true
-
isNaN()可以用于测试对象,首先会调用对象的valueOf()方法,然后再确定返回的值是否可以转换为数值
-
如果不能,再调用toString()方法,并测试其返回的值(这通常是ECMAScript内置函数和操作符的工作方式)
-
console.log(0/0); // NaN
console.log(-0/+0); // NaN
console.log(5/0); // Infinity
console.log(5/-0); // -Infinity
console.log(NaN/10); // NaN
console.log(NaN + 1); // NaN
console.log(NaN == NaN); //false
console.log(NaN === NaN); // false;
console.log(isNaN("10")); // false
console.log(isNaN("123a")); // true
console.log(isNaN("blue")); // true
console.log(isNaN(true)); // false true可以转为数值1
2.4.4、数值转换
有三个函数可以转换数值:Number() 、parseFloat() 、parseInt()
Number()可以用于任何数据类型 而后两个则主要用于将字符串转为数值
Number()
- 布尔值:true转为1,false转为0
- 数值:直接返回
- null:返回0
- undefined:返回NaN
-
字符串对应规则如下:
-
如果字符串中只包含数值字符,包括数值字符前的加、减号 则转换为一个十进制数值 (会忽略前面的0)
-
如果字符串包含有效的浮点值,则转换为相应的浮点值(忽略前面的0)
-
如果字符串为有效的十六进制格式如:"0xf"则会转为与该十六进制值对应的十进制值
-
如果是空字符串,则转为0
-
如果字符串包含除了以上情况外的其他字符则返回NaN
-
-
对象:调用valueOf()方法并按照上述规则转换。如果转换结果为NaN,则调用toString()方法,再按照转换字符的规则转换
console.log('Number(undefined):', Number(undefined)); // NaN
console.log('Number(null):', Number(null));// 0
console.log('Number("0011"):', Number("0011")); // 11
console.log('Number("01.1"):', Number("01.1"));// 1.1
console.log('Number("123a"):', Number("123a")); // NaN
console.log('Number("0xf"):', Number("0xf")); // 15
console.log('Number(""):', Number("")); // 0
console.log('Number("-011")', Number('-011'));// -11
console.log('Number("+011")', Number('+011')); // 11
console.log('Number("01a")', Number("01a")) // NaN
console.log("Number(' 1233.1'):", Number(' 1233.1'));// 1233.1 会忽略前面的空格
parseInt()
与Number()不同的是,parseInt()更专注于字符串是否包含数值,字符串前的空格会被忽略,从第一个非空格字符开始转换
- 如果第一个字符不是数值,加号或者减号,立即返回NaN;这就意味着空字符串也会返回NaN(这点于Number不一样)
- 如果第一个字符是数值、加号或者减号则依次检测每个字符,直到字符串末尾,或碰到非数值字符
- 如果第一个字符是数值,parseInt()也能识别不同的整数格式,如:以“0x”开头的十六进制
- 如果第一个字符是0并且紧跟着数字那么会被解释为八进制
- 也可以接收第二个参数,用于指定低数(进制数):
如:parseInt("0xAF", 16) //175
parseInt("AF", 16) // 175 (指定了16进制,则可以省略0x)
parseInt("AF") // NaN (没有指定底数,也没有带着0x)
console.log("parseInt('123blue'):", parseInt('123blue')); // 123
console.log("parseInt(''):", parseInt("")); //NaN
console.log("parseInt(22.5):", parseInt(22.5)); // 22
console.log("parseInt('0xf'):", parseInt('0xf')); //15
console.log("parseInt(' 1233.1a'):", parseInt(' 1233.1a')); //1233
console.log("parseInt('-010', 8):", parseInt('-010', 8)); // -8
parseFloat()
与parseInt()函数类似,从那个第一个非空格字符开始转换,解析到字符串末尾或者第一个无效的浮点数值字符为止;
意味着第一次出现小数点是有效的,第二次出现就是无效的了,此时剩余字符串都会被忽略
- 与parseInt()不同的是,始终会忽略开头的0,十六进制数值始终会返回0
parseFloat()只解析十进制数,不能指定底数
- 如果字符串表示整数,没有小数点或者小数点后面只有一个0,那么parseFloat()返回整数
console.log("parseFloat('1234blue'):", parseFloat('1234blue')); // 1234
console.log("parseFloat('0xA')", parseFloat('0xA')); // 0
console.log("parseFloat('22.34.5'):", parseFloat('22.34.5')); //22.34
console.log("parseFloat('0908.5'):", parseFloat('0908.5')); //908.5
console.log("parseFloat('3.125e7'):", parseFloat('3.125e7')); // 31250000
console.log("parseFloat(' 33.0'):", parseFloat(' 33.0')); //33
2.5、String
用于表示0或多个16为unicode字符序列
字符串可以用单引号、多引号、反引号表示
2.5.1、字符字面量
字符串数据类型包含一些字符字面量,用于表示非打印字符或者有其他用途的字符
\n 换行
\t 制表
\b 退格
\r 回车
\f 换页
\\ \
' 用于以单引号标示的字符串中
" 用于以双引号标示的字符串中
` 用于以反引号标示的字符串中
\xnn 以16进制编码nn表示的字符 (n为0~F) 如 \x41 等于 ‘A’
\unnnn 以16进制编码nnnn表示的unicode字符(n是0~F)如\u03a3 表示希腊字符Σ
以上字面量可以出现在字符串的任意位置
console.log('\u03a3'); //Σ
console.log("This is \u03a3".length); // 9 !!转义字符序列只算一个字符!所以是9个长度
2.5.2、字符串特点
字符串是不可变的。一旦创建,值就不能变了,要修改某个变量中的字符串值,必须先销毁原始的字符串
然后再将新值的另一个字符串保存到该变量中
如:
let lang = 'Java';
lang = lang + 'Script';
整个过程首先会分配一个足够容纳10个字符的字符空间,然后填充上Java和Script。
最后销毁 Java和Script字符串,因为这两个字符串没有用了。这也是一些早期浏览器拼接字符串非常慢的原因。
2.5.3、转换为字符串
有两种方式可以把一个值转换为字符串:
2.5.3.1、toString()
-
可用于数值、布尔值、对象和字符串值(字符串值的toString( )只是简单返回自身的一个副本)
-
null和undefined都没有toString()方法
-
多数情况下,toString()不接收任何参数,但是对数值使用的时候可以接收一个作为底数的参数;直接使用toString()返回的是数值的10进制字符串可以通过传递底数来输出数值的2进制、8进制、16进制字符串
-
如果不确定一个值是不是null或者undefined,可以使用String()转型函数,它始终会返回的是相应类型值的字符串:
-
如果值有toString()方法则调用该方法并返回结果
-
如果值是null则返回"null"
-
如果值是undefined则返回"undefined"
-
2.5.3.2、给一个值加上一个空字符串""
let num = 10;
console.log(num.toString(2)); // 1010
console.log(num.toString(8)); // 12
console.log(num.toString(16)); // a
console.log(num.toString(10)); // 10 -- 默认情况下不传递参数时的结果就是10进制的
let value1 = null;
let value2;
console.log(String(value1)); // null
console.log(String(value2)); // undefined
2.5.4、模板字符串
ES6新增,需要注意的是,模板字符串会保留反引号中的空格
// 模版字面量
let templateStr = `first line
second line`;
console.log(templateStr.length); // 38 保留了中间的空格
let templateStr2 = `
first line
second line`;
console.log(templateStr2[0] === '\n'); // 以一个换行符开头
const name = "qll"
const age = 18
const height = 1.88
const message = `my name is ${name}, age is ${age}, height is ${height}`
console.log(message)
const info = `age double is ${age * 2}`
console.log(info)
function doubleAge() {
return age * 2
}
const info2 = `double age is ${doubleAge()}`
console.log(info2)
2.6、Symbol
ES6新增
2.6.1、基本使用
const s1 = Symbol()
const s2 = Symbol()
console.log(s1 === s2) // false
2.6.2、Symbol描述(ES10新增)
const s3 = Symbol("123")
console.log(s3.description) // 123
2.6.3、Symbol作为key值
// 1.在定义对象字面量时使用
const obj = {
[s1]: "123",
[s2]: "456"
}
// 2.新增属性
obj[s3] = "789"
// 3.Object.defineProperty方式
const s4 = Symbol()
Object.defineProperty(obj, s4, {
enumerable: true,
configurable: true,
writable: true,
value: "10"
})
console.log(obj[s1], obj[s2], obj[s3], obj[s4])// 123, 456,789,10
// 注意: 不能通过.语法获取
// console.log(obj.s1)
!使用Symbol作为key的属性名,在遍历/Object.keys等中是获取不到这些Symbol值
需要Object.getOwnPropertySymbols来获取所有Symbol的key
console.log(Object.keys(obj))// []
console.log(Object.getOwnPropertyNames(obj)) // []
console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(), Symbol(), Symbol(123), Symbol()]
const sKeys = Object.getOwnPropertySymbols(obj)
for (const sKey of sKeys) {
console.log(obj[sKey]) //依次输出 123 456 789 10
}
2.6.4、Symbol.for(key)
根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol ,如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中
const sa = Symbol.for("aaa")
const sb = Symbol.for("aaa")
console.log(sa === sb) // true
2.6.5、Symbol.keyFor(sym)
用来获取全局symbol 注册表中与某个 symbol 关联的键
如果全局注册表中查找到该symbol,则返回该symbol的key值,返回值为字符串类型。否则返回undefined
const key = Symbol.keyFor(sa)
console.log(key) // aaa
const sc = Symbol.for(key)
console.log(sa === sc) // true