转换为数字类型
常用的应用场景
- 数学运算
- == 比较
- 手动转换
数学运算
除了"+"运算其他的都是数学运算,加号不仅仅是数学运算,还有字符串拼接
- "+" 两边都有值,有一边出现字符串或者对象,则为字符串拼接(有特殊性)
- 原理:调用 Object.prototype.toString 方法
- 适用范围:只有纯粹的对象才会转换为 [object Object]
- 原本是想把{}变为数字,但是Symbol.toPrimitive/valueOf/toString,调到toString变为字符串,此时符合了有一边变为字符串了,则是字符串拼接
- 特殊情况
- {}+10--> 10:{} 看做是一个代码块(ES6 块级上下文),真正做运算的只有 +10 ,所以最终结果是 10
- ( {}+10 ) --> '[object Object]10':用小括号包裹起来,里边的内容看做是一个整体
- 只有一边/ ++x/ x++ ,都是数学运算
- +'10' --> 10:number 类型
- 10+(++X):先把 X 累加 1,然后和 10 运算
- 10+(X++):先和 10 进行运算,最后再把 X 累加 1
思考 x++ == x+=1 == x=x+1? 分析
- let x = '10';console.log( ++x );
- 有一边则为数学运算,先变为数字类型 10,然后在 加 1,运算结果赋值给 x 为 11
- console.log( x+=1 );
- x+=1 --> x=x+1 --> '10'+1-->'101'
对象类型转换为数字类型
内部机制
- 首先检测对象的 Symbol.toPrimitive 这个属性,获取原始值
- Symbol.toPrimitive(hint):hint 检测到浏览器隐式规定的转换类型:'number'/'string'/'default'
- 如果没有这个属性,则继续调用它的 valueOf(),也是获取原始值(number/string/boolean/null/undefined/symbol/bigint)
- 如果值不是原始值,则继续调用 toString() 转换为字符串
- 再把字符串基于 Number(str) 转换为数字
其他类型转换为 number
Number 类型转换规则
Number( undefined ):NaN
Number( null ):+0
Number( true ):1
Number( false ):+0
Number( 数字 ):返回参数「不需要转换」
Number( symbol 类型 ):抛出类型错误的异常「symbol 不能直接转换为数字」
Number( bigint 类型):返回不带 n 的数字
Number( "" ):空字符串或者空格,返回 0
Number(-10):返回 -10
Number(+10):返回 10
除上述情况外,剩下的情况,只要字符串中出现非有效数字字符,结果为 NaN
隐式转换(手动转换)
- Number():浏览器内部默认要先转换为 Number 在进行计算的
- 数学运算
- 基于==比较的时候
- 有些值是需要转换为数字在进行比较
- isNaN([value]) 如果不是数字类型先转换成数字类型 ...
思考?
如何实现让其 obj - 10 = 0?
let obj = {
name: 'Guyal_',
[Symbol.toPrimitive]() {
return 10;
}
};
console.log(obj-10); // 0
分析分析
既然,对象存在 Symbol.toPrimitive 这个属性,又可以调用 valueOf/toString 方法,那么,我们完全可以重构这三种方法,以此来实现想要的结果。我们来看实现方法
底层调用 obj 的 Symbol.toPrimitive 属性,获取原始值,进而实现数学运算,最终结果为 0
parseInt/parseFloat?
parseInt
- 语法:parsetInt( [val],[radix] )
- 参数:
- [val]: 必须是一个字符串,如果不是,则也要默认转换为字符串
- [radix]:不设置(或者写的是零):正常都是按照 10 处理的,如果字符串是以 ”0x“ 开始的,默认值是 16...
- 返回值:整数
- 查找机制
- 从字符串的左侧第一个字符开始,查找有效数字字符(遇到非有效数字字符停止查找,不论后面是否还有数字字符,都不再找了),把找到的有效数字字符转换为数字,如果一个都没找到,结果就是 NaN
- 处理机制
- 先在[val]中,找到所有符合 [radix] 进制的内容
- 从左到右查找,直到遇到不符合的为止「不论后面是否还有符合进制的,都不在查找了」
- 进制转换
- 二进制「0~1」
- 三进制 「0~2」
- 四进制 「0~3」
- 八进制 「0~7」
- 十进制 「0~9」
- 十六进制 「0
9 A10F15」
- 然后再把找到的内容看做[radix]进制,转换为十进制
- [radix] 范围 2~36,除了 0 以外 (0->10/16),不在这个范围内,结果为NaN
parseFloat([val])
- 不支持第二个参数
- 比 parseInt 多识别一个小数点,其他与 parseInt 相同
对象类型转换为字符串类型
应用
- 字符串拼接
== 和 === 的区别?
- == 比较规则
- 两边类型不一致,则转换为相同的数据类型
- 对象 == 字符串 「对象会转换为字符串」
- 剩余的情况都是转换为数字
- 对象 == 数字
- 字符串 == 布尔
- 字符串 == 数字
- 布尔 == 数字
- 两边类型一致,则开始进行比较;对象比较的是堆内存地址
- 两边类型不一致,则转换为相同的数据类型
- === 比较规则
- 只要类型不一致,则不会转换,直接返回 false
插一波面试题
输出结果是?
console.log([]==false);
console.log(![]==false);
console.log([]==false);- 对象==布尔 都转换为数字(隐示转换)
- 对象转换为数字:
- [].valueOf 不是一个原始值,是引用类型的值,所以调用 [].toString() 方法,其结果为""(空字符串),
- 转换为数字
- "" 转换为 Number 类型为 0,false 转换为 Number类型是 0 ,所以[]==false ==> true
console.log(![]==false);- !的优先级高于==
- ![] 首先转换成布尔值,[]转换成布尔值为 true,取反则是 false,所以 false==false ==>true
- 除了 0/NaN/null/undefined/空字符串,转布尔值为 false,其余都为 true
- Boolean([]); true
- !true = false
实现如下代码,使其输出'OK'
var a = ?
if(a == 1 && a == 2 && a == 3) {
console.lo('OK');
}
- 第一类:隐式数据类型转换的时候进行处理
-
- 重构 Symbol.toPrimitive / toString / valueOf 均可
var a = { i: 0 } // 对象==数字,需要让对象转换为数字 a[Symbol.toPrimitive] = function() { return ++this.i } if(a == 1 && a == 2 && a == 3) { console.log('OK'); }-
- 数组 toString 方法重构(实现的小技巧)
var a = [1,2,3]; // toString 重写为 shift 方法,调用 toString 相当于调用 shift 方法,删除首项,并返回删除项,从而实现 a.toString = a.shift; if(a == 1 && a == 2 && a == 3) { console.log('OK'); } -
- 第二类:ES6 数据劫持
var i = 0;
Object.defineProperty(window, 'a', {
get() {
return ++i;
}
});
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
阿里面试题
let arr = [27.2, 0, '0013', '14px', 123];
arr = arr.map(parseInt);
console.log(arr);
思路
- 回调函数:把一个函数作为值传递给另外一个函数执行(实参)
- Map 使用方法
- 数据中有多少项,就迭代多少次,每一次执行回调函数(item当前迭代项 index索引),支持回调函数返回值,返回啥就把当前项替换成啥,原始数组不变,以新数组返回!! 分析:结果为:[27, NaN, 1, 1, 27]
parseInt(27.2,0) :27
parseInt(0,1):NaN
parseInt('0013',2):'001' 看做2进制 转换为10进制,1*2^0 -> 1
parseInt('14px',3):'1' 看做3进制 转换为10进制,1*3^0 -> 1
parseInt(123,4):'123' 看做4进制 转换为10进制
3*4^0 + 2*4^1 + 1*4^2 -> 3+8+16 -> 27