JavaScript类型可以分为两类:原始类型和对象类型。原始类型包括数值、字符串、布尔值、及特殊值(null和undefined)。除这些原始类型外的值都是对象。
1 数值
js 的主要数值类型Number用于表示整数和近似数。
1.1 二进制浮点数与舍入错误
js 使用的IEEE-754浮点表示法是一种二进制表示法,向0.1,0.01等二进制浮点表示法无法精确表示。经过js数值有足够大的精度,能非常近似地表示0.1,单无法精确地表示。
这可能会导致一些问题,比如:
let x = 0.3 – 0.2;
let y = 0.2 – 0.1;
x == y; // false,两个值不一样
x === 0.1; // false,无法精确到0.1
这些问题并不是js独有的问题,而是所有使用二进制浮点数的编程语言共有的问题。
2 文本
js中表示文本的类型是String。
js最早的版本要求字符串字面量必须写在一行,到ES5,可以在每行末尾加一个反斜杠(\)从而把字符串字面量写到多行上。这个反斜杠后它后面的行终结符都不属于字符串字面量。ES6的反引号语法支持跨行字符串,而行终结符也是字符串字面量的一部分。
// 写在一行但表示两行的字符串:
“hello \n word”;
// 写在两行单只有一行的字符串:
“hello \
word”;
// 写在两行实际也是两行的字符串:
`hello
word`;
2.1 模版字面量
ES6及之后的版本,字符串字面量可以用反引号来定界,这称为模版字面量:
let s = `hello word`;
模版字面量可以包含任意js表达式,可以是任意数量,将这些表达式的值转换为字符串,然后再把这些字符串与反引号中的字面量组合:
let name = ‘黄兮言’;
let str = `Hello ${name}`; // hello 黄兮言
如果在开头的反引号前面有一个函数名(标签),那么模版字面量中的文本和表达式的值将作为参数传给这个函数。“标签化模版字面量”的值就是这个函数的返回值。
let name = ‘js’;
‘hello ’.concat`${name} !`; // 返回值是:hello js!
2.2 模式匹配
js 定义了正则表达式(RegExp)的数据类型。
一对斜杠之间的文本构成了正则表达式字面量。这对斜杠的第二个后面也可以跟一个或多个字母,用于修改模式的定义。
let str = “hello js! this is very goods.”;
let pattern = /\bvery\b/; //匹配very这个单词
pattern.test(str); // true,是否存在匹配
str.search(pattern); //第一个匹配的位置
str.match(pattern); // [“very”] 所有匹配项的数组
3 null 与 undefined
null | 是“object”类型,表示某个值不存在。程序级别、正常或意料之中的空值。 |
undefined | 表示一种更深层次的不存在,类型是“undefined”。表示一种系统级别、意料之外或类似错误的空值。 |
图 null 与 undefined的区别
null 与 undefined在实际的开发中经常被混用。
4 符号(Symbol)
是ES6新增的一种原始类型,用作非字符串的属性名。
let sym = Symbol(“prop”);
typeof sym; // symbol
let o {sym: ‘’}; //定义sym属性
Symbol函数永远不会返回相同的值,比如 Symbol(“a”) !== Symbol(“a)”;
这个特性常用来为对象添加新的属性,这样就无须担心可能重写已有的同名属性了。
Symbol.for()与Symbol()不同,Symbol.for()会返回一个与该字符串关联的符号值。相同字符串始终返回相同的值。
let s = Symbol.for(“a”);
let t = Symbol.for(“a”);
s === t; // true
s.toString(); // Symbol(a)
Symbol.keyFor(t); // a
5 类型转换
js对象到原始值转换到复杂性,主要在于某些对象类型有不止一种原始值的表示。比如Date对象可以用字符串表示,也可以用时间戳表示。
偏字符串 | 返回原始值,而且只要可能就返回字符串。 |
偏数值 | 返回原始值,而且只要可能就返回数值。 |
无偏好 | 不倾向任何原始值类型,由类定义自己的转换规则。 |
表 js定义的3种原始值转换的基本算法
对象到布尔值到转换:不需要使用上面的转换算法。所有对象都转换为true,包括空数组,甚至new Boolean(false)这样的包装对象。
5.1 toString()和valueOf()方法
所有的对象都会继承这两个方法。
toString():返回对象的字符串表示。很多类都定义了自己特有的toString()版本。
valueOf():把对象转换为代表对象的原始值(如果存在这样一个原始值)。对象是复合值,且多数对象不能真正通过一个原始值来表示,因此valueOf()方法默认情况下只返回对象本身。
5.2 对象到原始值转换算法
偏字符串算法 | 首先尝试toString()方法,如果方法存在且返回原始值,则返回该值,否则尝试valueOf()方法,如果存在且返回原始值,则返回该值,否则转换失败,报TypeError。 |
偏数值算法 | 与偏字符串算法类似,先尝试valueOf()再尝试toString()。 |
无偏好算法 | 取决于被转换对象的类,如果是Date类型,则使用偏字符串算法,否则使用偏数值算法。 |
表 js 定义的3种原始值转换的算法实现
空字符串转换数值为0,只有一个元素的数组转换为该元素对应的字符串。
Number([]); // 0
Number([99]); // 99
6 var与let
ES6 推荐使用let来代替var,在这之前,声明变量的唯一方式是使用var关键字。var与let的重要区别如下:
1)使用var声明的变量不具有块作用域。var的作用域仅限于包含函数的函数体。
2)如果在函数体外部使用var,则会声明一个全局变量。与let声明的全局变量有重要区别。var声明的被实现为全局对象的属性。全局对象可通过globalThis引用,假如在函数外面写了var x = 2; 则相对于 globalThis.x = 2;
3)与let变量不同,var多次声明同名变量是合法的。
4)var声明有一个独特的特性是作用域提升,在使用var声明变量时,该声明会被提高到包含函数的顶部。但是变量的初始化仍在代码所在位置完成。也就是在函数某处定义了一个var变量,则可在函数的任意位置使用,而不会报错。