何为类型?-(并没有标准答案)类型是值的内部特征,他定义了值的行为,并区别各类值
类型
- 内置类型|七种
- 空值(null)
- 未定义(undefined)
- 布尔值(boolean)
- 数字(number)
- 字符串(string)
- 对象(object)
- 符号(symbol,ES6新增)
- 除对象外,其他类型统称为“基本类型”。
typeof与null
- typeof 运算符可以返回值的类型,一个字符串
- 但是这对于 null 并不适用。
- typeof null === “object”; // true
- 这很有可能会是一个永远无法修复的 js bug,因为牵扯太多 web 系统,修复它会产生更多 bug 而使得很多系统无法正常过工作。
- !a && typeof === "object"; //由此复合检测 null 值
- function 是 object 的一个子类型,但是 typeof 对于对象会返回 “function”
- 其中的 call 属性让他可以被调用,函数可以被理解为“可调用对象”
- 函数的 length 是其声明的参数个数
- array 是 object 的一个子类型,用数字顺序来进行属性索引的对象
类型与值
类型是“值”的定义,而变量是没有类型的,变量可以随时持有任何类型的值。
- undefined 与 undeclared
- 当变量不持有值为 undefined
- 变量不存在,没有声明过的变量是 undeclared,不过浏览器会报错 x is not defined。但不意味着这个报错与 undefined 有关。
- typeof 对于 undeclared 的未声明的变量返回 undefined,由于 typeof 的独特安全防范机制。这使得 typeof 可以用来判断变量是否被定义且没有报空指针错误的风险。
值
数组
- tip:delete 运算符可以在数组中删除一个单元,但是数组的 length 属性并不会减一。
- 数组通过数字进行索引,但是仍然可以像普通对象一样加入正常的键值,但是不会被 length 算入。
字符串
- 字符串与字符数组有很多相似之处,但并不意味着字符串的实现就是一种数组。
- 字符串作为一种基本数据类型是不可变的,而数组作为引用数据类型是可变的。
- 字符串的成员函数不会改变原始值,而是创建并返回一个新的字符串,而数组的成员函数则直接在原始值上操作。
- 数组的函数用来处理字符串很方便,比如 join map,但是字符串并没有这些函数,可以使用 call 来实现。
- 但是对于改变原始数组的成员函数,如果字符串本身没有则无法借用,比如反转函数 reverse();
- 对于这种情况可以吧字符串先用 split 转为字符数组,操作后在使用 join 拼接回字符串。
- 这并不适用于含有复杂字符的字符串。比如 unicode 或其他多字节字符。
number
- js 只有 number 一种数字类型,并没有真正意义上的整数。42.0 与 42 在 js 看来是完全相等的。
- js 的数字基于 IEEE 754 标准实现,双精度格式。(64位)
- 语法
- 42.0 = 42
- 0.42 = .42
- 5E10 = 50000000000
- Number.prototype
- 数字值可以使用 Number 对象封装,所以数字值可以调用 Number.prototype 的方法。
- toFixed 返回指定小数位数的字符串表示,toPrecision 指定有效数位的显示位数。
- .运算的点号是一个有效的数字常量字符,他被认为是值的一部分优先级高于认为是运算符
- (42).toFixed.| 0.42.toFixed | 42..toFixed 是正确的
- 42.toFixed 错误
- 机器精度
- 由于 IEEE 754 规范对于小数的处理精度问题,这个误差值已经被存入 Number.EPSILON。
- 使用 Math.abs(a,b) < Number.EPSILON 来判断两个小数是否相等。
- 对于整数,有安全范围 2^53-1,最大整数与最小整数被定义为 Number.MAX_SAFE_INTEGER。
- 有些特殊操作只能操作32位,如与或运算之关心前32位,后面的部分会被忽略。
- NaN | not a number
- 理解为失败的数值或坏数值
- NaN 是唯一一非自反值(NaN === NaN // false),使用 Number.isNaN 来确定一个值是否是NaN
- Infinity | 无穷数
- 除以0
- 数字变成无穷数是不可逆的
- 自除不为0,是一个未定义操作,得NaN
- Object.is(a,b) | 用这个做比较可以处理上述所有情况!
值与引用
- js 对值传递或引用传递在语法上没有区别,完全由语言自身根据值的类型来决定。
- 对象和函数是引用传递,其他为值传递。
- 更多见往期文档,数据类型相关。
原生函数
- String()
- Number()
- Boolean()
- Array()
- Object()
- Function()
- RegExp()
- Date()
- Error()
- Symbol()——ES6 中新加入的!
他们可以使用 new 操作符来当作构造函数使用,且构造结构为 object 而非对应的基本数据类型。
内部属性class
通过 Object.prototype.tostring 访问到的 [object class],其中第二个字段即为对象内部的 class 属性值。
封装对象包装
- 基本类型并没有 .length 等等属性和方法,所以在调用时 js 会自动的创建一个基本类型的包装对象并调用对应的方法。但是能无需提前封装来“做优化”,js 引擎已经做过了相关优化,自己封装反而会降低性能。
- 一般不推荐使用封装对象,例如 new Boolean(false) 在 if 中为 true,但某些特定时候也能派上用场。
- 使用 ValueOf() 来得到封装对象中的基本类型值。
强制类型转换
值类型转换
- 将值的类型转换为另一种类型被称为显式类型转换。隐式类型转换又称为强制类型转换
- 强制类型转换总是返回标量的基本类型值,而不会返回封装对象或函数
- 另一种区分方式,静态类型语言的编译阶段的转换称为显式类型转换,运行时发生的类型转换称为隐式类型转换。
抽象值操作
ToPrimitive,抽象操作,toXXX 都属于抽象操作 - 先检查值是否有 valueOf() 方法,如果有则调用其返回基本类型值 - 如果没有则使用 toString() 返回 string ,再来进行强制类型转换为目标基本类型值 - 如果两个方法都没有返回基本类型值,则返回 typeError 错误 - Object.create(null) 创建的对象没有原型,也没有上述的两个方法,所以无法进行强制转换
转字符串
- toString
- 对于普通对象来说,toString 返回内部属性 class 的值。eg:[object object]
- 数组的 toString 已经被改写了,将所有的单元转为字符串再用逗号链接
- JSON.stringify
- 对大多数简单值来说,与 toString 基本相同
- 所有安全的 JSON 值都能用它进行字符串化
- 不安全的 JSON 值有 undefined symbol function 或循环引用等
- 定义一个 toJSON 方法来返回一个安全的 JSON 值然后再调用 sttingify 来字符串化
- stringify 的第二个参数很有用
- 如果是数组,则必须是字符串数组,序列化时只会关注这些字符的属性名
- 如果是函数,则参数为键和值,return 为处理后的值,序列化前会通过函数处理一次值
- 第三个参数为缩进格式
- 如果是数字则一次缩进为多少个空格
- 如果是字符则用这个字符代替一个缩进
转数字
-
ES5规范:true-1,false/null-0,undefined-NaN
-
ToNumber
- 对字符串处理为数字,如果字符串不符合数字规范则返回 NaN
- 但是对于0开头的字符串并不按16进制处理
-
对象会先抽象转换成基本类型值,如果基本类型不是数字在进行一次转换
转布尔
- 假值
- undefiend
- null
- false
- +0,-0,NaN
- “”
- 假值转换结果为 false,除此之外都是假值,包括封装了假值的对象也都是真值
- 假值对象
- 并非封装了假值的对象,而是一种 js 语法之外的外来值
- 例如 doucument.all,则是假值,因为它已经被废止
显式转换
- 运算符
- 一元运算符 + 可以作为 Number 的转换,例如 +“42” 会被转换为 42
- 5+ +‘3.14’ = 8.14
- 有很多运算符可以进行显示转换,但是这不利于代码可读性,比如 ~
- 一元运算符 + 可以作为 Number 的转换,例如 +“42” 会被转换为 42
- 解析数字字符串 parseInt
- 从左到右将字符转换为数字,遇到非数字字符则停止
- parseInt('42px') = 42
- 不要尝试传入非字符串,这是在自找麻烦
- 转布尔
- 可以用 !!a 来将 a 转换为 布尔。
- 其他的和常用方法差距不大
隐式转换
扬长避短,取其精华,弃其糟粕
字符串与数字
- + 加法运算
[1,2] + [3,4] = "1,23,4"- 抽象操作没有得到基本类型而被 toString 后拼接字符串
- ` 42 + '' = '42'
- - 减法运算
'3.14' - 0 = 3.14
|| 和 && 与其他语言不同的是,在 js 中与或运算并不返回布尔,而是两个值中的一个
- 对第一个操作数执行布尔判断
- || 第一个数布尔转换后为 true,则返回第一个值
- && 第一个数为true,则返回第二个值
else
对于 symbol,支持显式转换为 string,隐式转换则报 typeError
宽松相等== 与 严格相等===
过往的认知形容前者是比较值,后者是类型和值,可惜这是错误的。
正确的解释是前者允许在比较时进行强制类型转换,后者不允许
- 性能
- 根据错误的解释似乎严格相等工作量更大,因为进行了类型的比较
- 实际上宽松相等更大,因为进行了强制类型转换,但是这个事情的性能消耗仅仅是微秒级,可忽略不计。
- 他们都会比较类型,只不过对于类型不同的情况下采取了不同的处理
- 抽象相等|这种宽松相等算法正是隐式强制类型转换被人诟病的原因
- 如果两个值同等类型,则仅比较他们是否相等
- NaN 不等于自己
- +0 等于 -0
- 对象则比较是否指向同一个对象而非值比较
- 布尔与其他类型:先将布尔转为数字01,变成数字与其他类型的规范
- 字符串与数字:将字符串转为数字
- null 与 undefined 在 == 中相等,可用 == null 来代替 === null || === undefined
- 对象与非对象:将对象抽象操作后比较
- 对于null,undefiend,NaN,a == Object(a)为 false。因为他们没有封装对象,object 返回一个常规的空对象。
- 重写 valueOf 来改变抽象操作结果,仅仅用于面试就行,开发中用出来属于是脑淤血
- 如果两个值同等类型,则仅比较他们是否相等
- 扬长避短中的短
- 避免 == false,以下比较都是 true
- "0" == false
- false == 0
- false == ""
- false == []
- 以下也是 true,涉及 [],"",0 的尽量避免的情况
- "" == 0; //true
- "" == [];
- 0 == []
- 避免 == false,以下比较都是 true
抽象关系比较
- 比较双方先进行抽象操作
- 如果量变都是字符串则按照字母顺序依次比较,知道第一个不想等的字母出现
- 如果不符合两边都是字符串,则两边都转为 number 进行比较
语法
- 语句有结果值,仅供了解目前没啥用;
- 语句可以带标签
- {} + []; //0 代码块的坑
- js其实没有 else if,实际上后面的 if 作为一个单独的语句跟在了 else 后面。
运算符优先级
- 例如 && 优先级高于 ||
优先级参考链接 - 运算符多了依然建议通过()来确定,需要保证代码可读性
TDZ
typeof 对于未声明变量返回 undeifend 不会报错,但是在 TDZ 暂时性死区中会报错。
try .. catch .. finally
如果 try 中发生了终止,比如 return 或 throw,则 finally 会在终止语句之前触发。
如果 finally 发生了终止,则原来 try 中的终止语句会被抛弃。