个人笔记
本文详细说一下各种数据类型之间相互转化规则
需求与遇到的情景基本上都是把其他类型转换成Number,Boolean,String这几种数据类型
其它数据类型转换为Boolean
统一遵循以下规则:
只有0、NaN、null、undefined、''(空字符串)这五个值会转换为false,其余都转换为true
转换方法:
Boolean([value])!![value]![value]转换为布尔类型取反- 条件判断 例如:
if(1){},括号中的条件会转换 注意:
A||B,A&&B不会对数据进行转换,他们的返回值是A或B中的一个
例子:
console.log(!![])//true
console.log(!!-1)//true
把其它数据类型转换为String
分为显示转换和隐式转换。
显示转换使用String方法进行转换。隐式转换是使用+号运算符,+号两边有一边出现字符串,就会把另一边也转换为字符串并且拼接。
以上两种方法在转换时都遵循以下规则(即转换为字符串之后的结果):
- 如果是原始值转换结果则是相当于直接用引号将原始类型包起来(注意:
Bigint会去除n) - 如果是对象转换结果则比较特殊,下面详细说
分开讨论
原始值转换结果
规则:原始值转换结果是直接用引号包起来(注意: Bigint会去除n)
显式转换,即使用String方法时:
String(1)//"1"
String(false)//"false"
String(null)//"null"
String(undefined)//"undefined"
String(Symbol(1))//"Symbol(1)"
String(12312312312323534523423423n)//"12312312312323534523423423"
隐式转换,即使用+拼接
1+''//"1"
false+''//"false"
null+''//"null"
undefined+''//"undefined"
12312312312323534523423423n+''//"12312312312323534523423423"
注意:
- Bigint:
Bigint会去除n.- 如果不加n,一个普通的大数字转换成字符串结果会出错
Symbol(1)+''Symbol(1)+1都会报错,即Symbol类型无法进行隐式转换
对象转换
规则:不是所有对象都是字符串拼接
- 先去调取对象的
[Symbol.toPrimitive]属性值,如果没有这个属性 - 再去调取对象的
valueOf()获取原始值,如果不是原始值 - 再去调用对象的
toString()转换为字符串「如果是想转换为数字,则还会调用Number处理」
Object.prototype.valueOf() MDN
Object.prototype.toString() MDN
console.log(10 + [10, 20]); //->"1010,20"
为什么会出现以上这样的情况?原因是Symbol.toPrimitive为undefined,valueOf返回的也不是原始值,所以最后返回的是toString(),是一个字符串,所以在遇到+号就变成了字符串拼接
如果我们重写Symbol.toPrimitive:
const x = [10,20]
x[Symbol.toPrimitive] = function (){ return 'reWrite'}
String(x)//"reWrite"
x+''//"reWrite"
10+x //"10reWrite"
而以下情况:new Number(10).valueOf()有原始值的(10),所以最后是加法
const ten = new Number(10)
ten[Symbol.toPrimitive]//undefined
ten.valueOf()//10
10+ten//20
console.log(10 + new Number(10)); // ->20 new Number(10).valueOf()有原始值的
下面Date比较特殊
String(new Date())//"Sun Feb 28 2021 15:49:52 GMT+0800 (中国标准时间)"
new Date().toString()//"Sun Feb 28 2021 15:50:10 GMT+0800 (中国标准时间)"
new Date()+''//"Sun Feb 28 2021 15:47:47 GMT+0800 (中国标准时间)"
''+new Date()//"Sun Feb 28 2021 15:47:59 GMT+0800 (中国标准时间)"
+new Date()//1614498482848 特殊
const time = new Date()
time[Symbol.toPrimitive]('number')//1614506495466
time[Symbol.toPrimitive]('string')//"Sun Feb 28 2021 18:01:35 GMT+0800 (中国标准时间)"
time[Symbol.toPrimitive]('default')//"Sun Feb 28 2021 18:01:35 GMT+0800 (中国标准时间)"
重写Symbol.toPrimitive:
let obj = {x: 10};
console.log(10 + obj); // ->"10[object Object]"
let obj = {
x: 10,// obj[Symbol.toPrimitive] && valueOf && toString
[Symbol.toPrimitive](hint) {
console.log(hint); // 有这几个值,=>”default“、”string“、”number“,是浏览器为了识别帮我们转换成什么的一个参数,如果不知道就是default(这里就是),如果返回的明确知道是string,就是"string",因为加号不一定是什么,有可能是字符串拼接,有可能是加法运算,所以是default
return this.x;
}
};
console.log(10 + obj); ->20
注意:toString()方法也可以主动调用
Object.prototype.toString()//"[object Object]"
String({a:1})//"[object Object]"
''+{a:1}//"[object Object]"
'xxx'+{a:1}//"xxx[object Object]"
({a:1})+''//"[object Object]"//如果用括号括起来就跟普通String一样
String({})//"[object Object]"
({}).toString()//"[object Object]"
''+{}//"[object Object]"
({})+''//"[object Object]" 如果用括号括起来就跟普通String一样
这里是因为数组的toString方法被重写了
String([1,2])//"1,2"
[1,2]+''//"1,2",这里不需要括号括起来,因为本身就不会被识别成代码块{},本身就是一个变量
''+[1,2]//"1,2"
[1,2].toString()//"1,2"
String(function x(){})//"function x(){}"
''+function x(){}//"function x(){}"
function x(){}+''//0 特殊
(function x(){})+'' //"function x(){}"
特殊情况:左侧的{}当做代码块,不参与运算
把左侧的{}当做代码块,不参与运算,运算的只有 +n ,相当于将其转换为数字,下面也会说明
{a:1}+''//0 (特殊)
{a:1}+'123xyz'//NaN (特殊)
{a:1}+'123'//123 (特殊)
{}+''//0 (特殊)
function x(){} +''//0 特殊
function x(){ return 1} +'' //0 特殊
function x(){ } +'123'//123 特殊
总结
总结:把其他类型转换为字符串,结果一般都相当于""直接包起来,只有{}普通对象调用的是其原型上的toString()方法(Object.prototype.toString()),不过这个方法如果不重写,返回的一般是其数据类型,如"[object Object]",像数组,其toString方法就被重写了
额外:基于alert/confirm/prompt/document.write...这些方式输出内容,都是把内容先转换为字符串,然后再输出的
附加:+号转换为字符串的一些特殊情况
+号转换和对象转换为字符串有一些特殊情况,下面来说明
+号转换特殊情况 1:+只有一边
let n = '10';
console.log(+n); // 10 ->转换为数字
console.log(++n); //11 ->转换为数字然后累加1
console.log(n++); // 11 ->转换为数字然后累加1
i++ 和 i=i+1 以及 i+=1 三个是否一样?
i=i+1&i+=1是一样的i++一定返回的是数字 但是i+=1就不一定了,有可能是字符串拼接
let i = 10;
console.log(5 + (++i)); //先i累加1,累加后的结果运算 输出 16 ,i->11
console.log(5 + (i++)); // 先运算 再累加1 输出15, i->11
+号转换特殊情况 2:+有一边出现对象
let n = 10;
{}+n// -> 10 把左侧的{}当做代码块,不参与运算,运算的只有 +n
n+{} //-> '10[object Object]' 字符串拼接
其他类型转化为Number
分为Number()型和parser型转换
Number([val])parseInt([val])/parseFloat([val])
Number()型转换
可以直接手动调用Number([val])进行转换,也可用于隐式转换。
隐式转换有以下几种情况会调用Number()
isNaN([val])- 数学运算
+ - * / %(特殊情况:+在出现字符串的情况下不是数学运算,是字符串拼接) - 在
==比较的时候,有些值需要转换为数字再进行比较
Number()的转换机制(结果)如下
- 字符串->数字 :
''(空字符串)变为0,字符串中只要出现非有效数字字符结果就是NaN,有正常数字就转化为正常的数字 - 布尔->数字 :
true变为1false变为0 null->数字:0undefined->数字:NaNSymbol->数字:报错BigInt->数字:正常转换- 对象遵循
Symbol.toPrimitive=>valueOf=>toString=>Number的规则,会先经过前三步转化为字符串,再调用Number方法
以下是原始值举例:
Number('')//0
+''//0
-""//-0
*''// Uncaught SyntaxError: Unexpected token '*'
1*''//0
1/''//Infinity
Number('1xxx')//NaN
+true//1
-true//-1
+false//0
-false//-0
Number(true)//1
Number(null)//0
Number(undefined)//NaN
+null//0
+undefined//NaN
Symbol(1)+1//Uncaught TypeError: Cannot convert a Symbol value to a number
Number(182391823798473394859034n)//1.8239182379847338e+23
Number(BigInt(10))//10
182391823798473394859034n+1//Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
182391823798473394859034n+1n//182391823798473394859035n
对象变为数字,应该先valueOf(),没有原始值,再toString()转化为字符串,在调用Number()方法
如下所示
const a = {x:1}
console.log(a+1)//"[object Object]1"
先valueOf(),没有原始值,再toString()转化为字符串,转化为字符串就直接拼接了
对象转换实验:
const a = {x:1}
console.log(a[Symbol.toPrimitive])//undefined
console.log(a.valueOf())//{x: 1} 返回对象本身,不是原始值,
console.log(a.toString())//"[object Object]"
console.log(a+1)//"[object Object]1"//对象变为数字,应该先`valueOf()`,没有原始值,再`toString()`转化为字符串,在调用`Number()`方法
console.log(Number(a))//NaN
console.log(String(a))//"[object Object]"
const a = new Number(10)
console.log(a[Symbol.toPrimitive])//undefined
console.log(a.valueOf())//10 返回数字类型原始值,
console.log(a.toString())//"10" 返回原始值的字符串
console.log(a+1)//11 返回数字类型
console.log(Number(a))//10
console.log(String(a))//"10"
总结 : js中,加号两边出现字符串,则变为字符串拼接(特殊性)。如果出现对象也变为字符串拼接(因为原本应该吧对象转换为数字,但是对象转数字需要先转换为字符串,遇到+号则拼接为字符串拼接 例如 1+[]//"1")
parser型转换
parseInt([val])/parseFloat([val])
parseInt机制:先转化为字符串,从字符串左侧第一个字符开始,查找有效数字字符,遇到非有效数字字符就停止查找,不论后面是否还有数字字符,都不再找了,把找到的有效数字字符转换为数字,如果一个都没找到,结果就是NaN.parseFloat会多识别一个小数点
parseInt('')//NaN
Number('')//0
isNaN('') //false ,相当于isNaN(0)
parseInt(null) //NaN
Number(null)// 0
isNaN(null)//false相当于isNaN(0)
parseInt('12px')//12
Number('12px')//NaN
isNaN('12px')//true
JS中验证两个值是否相等
JS中验证两个值是否相等
==:相等
如果两边类型一样:那就直接比较,注意特殊的几个:
{}=={}=>false对象比较的是堆内存的地址[]==[]=>falseNaN==NaN=>false
如果两边的类型不一样,首先会隐式转化为相同的类型,然后再做比较,规则如下(注意,只有==左右不一样才会进行转换,===类型不一样直接就是false)
-
对象==字符串要把对象转换为字符串 -
null==undefined->true「三个等于号是不相等的」,但是null/undefined和其它任何值都不会相等 -
NaN==NaN->false -
Symbol()==Symbol()->false -
剩余的情况「例如:
对象==数字、字符串==布尔...」都是要转换为数字,再进行比较的例子:
[]== false//true
对象==布尔,都转化为数字(隐式转换),[]转化为数字,先基于valueOf()获取原始值,没有没有原始值,再调用toString()=>'',再转化为数字0,false转化为数字,也是0
===:绝对相等
「要求两边的类型和值都要相等 例如:switch case」
Object.is([val],[val])
面试题
parseFloat('1.6px') + parseInt('1.2px') + typeof parseInt(null) //"2.6number"
//1.6+ 1+ 'number'
isNaN(Number(!!Number(parseInt('0.8'))))//false
过程:
parseInt('0.8')//0
!!Number(0)//false
Number(false)//0
isNaN(0)//false
10+false+undefined+[]+'Tencent'+null+true+{}//"NaNTencentnulltrue[object Object]"
10+false //10
10+undefined// NaN 10+NaN=>NaN
NaN+[] //'NaN' 因为[]要先转化成字符串'',然后拼接为'NaN'
'NaN' +'Tencent'+null+true+{} //"NaNTencentnulltrue[object Object]" 全部都是字符串拼接
题2
如何实现打印出ok
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
var a = {
i: 0,
// a[Symbol.toPrimitive] 还可以重写:valueOf/toString
[Symbol.toPrimitive]() {
// this->a
return ++this.i;
}
};
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
var a = [1, 2, 3];
a.toString = a.shift;
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
// 解决思路2:我们可以劫持对象的成员访问
// + 全局下声明的变量是window的一个属性
// + Object.defineProperty数据劫持的办法
let i = 0;
Object.defineProperty(window, 'a', {
get() {
return ++i;
}
});
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
parseInt困难面试题深度解析
let arr = [10.18, 0, 10, 25, 23];
arr = arr.map(parseInt);
console.log(arr);
arr = arr.map((item, index) => {
// 循环遍历数组中的某一项就会触发回调函数
// 每一次还会传递当前项和当前项的索引
});
parseInt([value],[radix])
-
[radix]这个值代表进制,不写或者写0默认都按照10处理,即十进制.(特殊情况:如果[value]是以0X开头,则默认值不是十进制而是十六进制) -
进制有一个取值的范围:2~36之间,如果不在这之间,整个程序运行的结果一定是
NaN -
把
[value]看做[radix]进制,最后把[radix]进制转化为十进制 -
从字符串左侧第一个字符开始查找,找到符合
[radix]进制的值(遇到一个不合法的,则停止查找),把找到的值变为数字,再按照把[radix]转换成为十进制的规则处理
parseInt('10.18',0)
// '10' -> 10
parseInt('0',1) // =>NaN
parseInt('10',2)
// 10 把它看做2进制,最后转换为10进制
// 1*2^1 + 0*2^0 => 2
parseInt('25',3)
// 2 当做3进制转换为10进制
// 2*3^0 => 2
parseInt('23',4)
// 23 当做4进制转换为10进制
//2*4^1 + 3*4^0 => 11
把一个值转换为十进制
位权值:每一位的权重,个位是0,十位是1...
147(八进制) => 十进制
1*8^2 + 4*8^1 + 7*8^0
12.23(四进制)=> 十进制
1*4^1 + 2*4^0 + 2*4^-1 + 3*4^-2
参考: