JavaScript强制类型转换、== 和 ===
值类型转换
JavaScript值的强制类型转换总是返回基本类型值。如:字符串、数字和布尔值,不会返回对象和函数。如果被转换的值是对象,则进行抽象操作ToPrimitve
。ToPrimitve
操作会首先检查是否有valueOf
方法,如有且返回基本类型的值,则使用该方法进行强制类型转换。如没有valueOf
方法就调用toString
的返回值来进行类型转换(前提是有toString
方法)。
ToString、ToNumber、ToBoolean
转化为字符串
- 1.1 其它类型的值转化为字符串的基本规则如下:
需要被转化的值 | 转化为字符串 |
---|---|
null | "null" |
undefined | "undefined" |
true | "true" |
false | "false" |
数字 | 查看👇🏻备注 |
数组 | 数组中的每一项转化为字符串,并通过,分隔符 拼接在一起 |
对象(非数组) | 调用toString() 方法 |
下面是一些常见的🌰
// 数组转化为字符串-1
var arr = [1,2,3];
var str = arr + '';
console.log('str: ', str);
// 控制台打印的值为 str: 1,2,3
// 数组转化为字符串-2
var arr1 = [{ name: 'ohYang', value: 1 },2,3];
var str1 = arr1 + '';
console.log('str1: ', str1);
// 控制台打印的值为 str1: [object Object],2,3
// 非数组对象转化为字符串
var obj = { name: 'ohYang', value: 1 };
var str2 = obj + '';
console.log('str2: ', str2);
// 控制台打印的值为 str2: [object Object]
备注:极大值和极小值数字使用指数形式转化为字符串显示;浮点数转化为字符串时,字符串在拼接的过程中会自动去除浮点数后面多余的0。
var numMax = 2 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000;
numMax.toString(); // "2e21"
// 数字num转化为字符串,浮点数后面多余的零会被去除
var num = 10000.00
var numStr = num + '';
console.log(numStr)
// 控制太打印的值为:strNew: 1000
- 1.2 JSON字符串化
JSON字符串化,并非强制意义上的强制类型转换,但在大多情况下,也用到了
toString()
。能成功JSON
字符串化的值,必须是安全的JSON
值。
不安全的JSON值:
undefined
、function
、symbol
、循环引用
(对象之间的互相引用,形成一个无限循环)。不安全的JSON
值在进行JSON.stringify()
操作中会忽略不安全的JSON
值。基础类型的值只有undefined
为不安全的JSON值
🌰 基础类型的值JSON
值进行JSON.stringify()
操作
/* 定义一个方法 */
function printJsonValue(val) {
let jsonVal;
jsonVal = JSON.stringify(val);
//第一个打印值,第二个打印的是第一个值的类型
console.log(jsonVal, typeof jsonVal);
}
// undefined null true false NAN
printJsonValue(undefined); // 打印: undefined 'undefined'
printJsonValue(null); // 打印:null 'string'
printJsonValue(true); // 打印:true 'string'
printJsonValue(false); // 打印:false 'string'
// NaN JSON字符串化的结果是 'null'
printJsonValue(NaN); // 打印:null 'string'
🌰 对象(不含数组)中不安全JSON
值进行JSON.stringify()
操作
对象的属性所对应的值如果是
undefined
、函数
的时候,JSON.stringify()
会忽略该部分,如下🌰所示
var obj = {
a: null,
b: undefined,
c: false,
d: 123,
e: function() {}
}
vat jsonObj = JSON.string(obj);
// jsonObj的值如下:
// '{"a":null,"c":false,"d":123}'
像上🌰这种情况,对象的某些属性的值含有非安全的JSON
值。如果还是需要对该对象进行JSON
字符串化,需要在对象的内部定义一个toJSON
方法。(注意:toJSON
本身方法是返回一个安全的JSON
值而不是JSON
字符串)
let demo = {
a: 'hello-world',
b: undefined,
c: function() {},
// 返回一个安全的JSON值
toJSON: function() { return this.a };
}
let jsonDemo = JSON.stringify(demo);
console.log('jsonDemo: ', jsonDemo);
// 打印结果:jsonDemo: "hello-world"
🌰 _非空数组 _中不安全JSON
值进行JSON.stringify()
操作
:::warning
数组中非安全的JSON
值会返回null
,从而保证被该数组中的其它值的位置值不会因为非法的JSON
值而改变
:::
var arr = [ null, undefined, false, 123, function() {} ]
var jsonArr = JSON.stringify(arr);
/* jsonArr的值如下:
arr 中的非安全的JSON值如undefined、函数等会返回null
'[null, null, false, 123, null]'
*/
注: 对象循环引用使用JSON.Stringify
会报错 “TypeError: Converting circular structure to JSON”
var obj = { key: 'haha' };
var a = {
key: 'hello world',
x: obj,
y: function() {}
};
obj.z = a;
JSON.stringify(a);
// 在浏览器的控制台输入上面的代码,并执行,会报出上文的错误
ToNumber
基本情况如下表格
被转化的值 | 转化的结果 |
---|---|
null | 0 |
undefined | NAN |
true | 1 |
false | 0 |
NaN | NaN |
对象(包含数组) | ToPrimitive操作,包含valueOf() 和toString() |
注:如进行ToNumber
操作,调用valueOf()
和toString()
都无法最终得到基本类型的值,则会报错typeError
。
ToBoolean
- 假值:
null
、undefined
、false
、+0
、-0
、NAN
、''
; - 真值:除假值之外的值都是真值(也就是除去上面的假值之外的值都是真值);
强制类型转换
转化为数字
常用方法如下:
// value 表示需要转化Number的值,
Number(value)
// 使用 + 转化为数字,可以简化代码。当然也可以使用 减号 - 、乘号 x、除号达到相同的目的
+value;
注:如果使用了+
符号前置将一个值转化为Number,为了不让代码有歧义或是难以理解,不要与++
、--
、+=
、-=
以及其他运算符号交织在一块使用。
var a = 10, b = '9', c = '8';
var d = a + +c; // 不建议使用这样写
var e1 = +c; var e2 =+ c; // 不建议,虽然得到的e1 e2的值都是Number
var f = a + ++e2; // 不建议
使用+
加号运算符,可以将一些数字字符串转化为Number
,例如上面代码中的示例
只有当
x1
+x2
都为数字的情况下,+
作为加号运算符而非字符串拼接符。
var x = x1 + x2;
一些常见的坑(详细查看注释部分的文字说明)。
var x = [1] + [2];
// x 的最终值是 '12'
/* 上例 加号连接的两个值,遵循上面的规则,[1] [2]都是非Number类型的值,则
+ 就是字符串的拼接符,则必须把两个数字都转化为string然后再拼接
(上例的数组最终转化为string先调用valueOf()再调用toString() 方法)
*/
{} + [];
/*
首先要明白 {} 放在语句的句首,是表示为这为一个『空的代码块』而非『空对象』。
既然是空的代码块,就可以当成是不存在的部分,则 {} + []; 中的 + 运算符,则是把
[]强制转化为number,等价于Number([])值为0;
*/
转化为字符串
常见的转化字符串的三种基本方式如下代码所示
// value 表示需要转化成字符串的值,
String(value);
value.toString();
// 实际中为了更方便快捷使用 value + ''来强制转化字符串
var targetStr = value + '';
value + ''
以及String(value)
都会把目标变量转化为字符串,但是两者之间还是有区别。前者的value
会先调用内置的valueOf()
方法、而后再调用内置的toString()
方法。后者是直接调用内置的toString()
方法。
/* 通过 +,将其它值强制显式转化为Number; */
var val = '100';
var valNum = +val;
// 等价于 Number(val)
var val1 = 199;
var sum = val1 + +val;
// 建议使用 Number(val) 代替 +val
上面的sum的值,可以很轻易的得出是299,但是如果当 + ,一元运算和其他运算符在同一个语句中使用,避免造成理解困扰,建议使用Number(value)代替
var x = '10', d;
d = +x;
d
的值是数字10,但是上面的写法似乎容易造成误解,毕竟很容易与d += x
、d -= x
搞混,从而增加代码的理解难度。
使用+
号连接字符串,是构成新的字符串较常用的方法,只要+
连接的两个操作值不都为number,则+
作为字符串拼接符号而非加号运算符;下面看一些例子
注:两个操作值之一或是同为null、undefined、false、true、NAN,不在本次的讨论范围。
// 🌰1
var str = 'hello' + 'world';
// + 做为两个字符的拼接符
// 🌰2
[] + {};
// 结果是 '[object Object]'
/*
🌰2 解析:
+ 左右的 []、{} 分别为数组和空对象
[]: 调用valueOf(),然后再调用toString() 得到一个空的字符串
{}: 调用valueOf(),然后再调用toString() 得到一个字符串 '[object Object]'
所有最终的结果是'[object Object]'
*/
转化为布尔值
最常用的方式
// value 表示需要转化成布尔值的目标值,
Boolean(value);
// 实际中为了更便捷多使用 ! 取反符号、!! 两个取反符号 来强制转化成布尔值
var valueBoolA = !!value;
var valueBoolB = !value;
最常见用到布尔转换的是在条件判断语句中
if (conditiobn) { /* do something */}
括号内部内部的
condition
可以是变量也可以是表达式
if (condition) {
console.log('print is true');
} else {
console.log('print is false');
}
上面的代码括号内,虽然没有!
、!!
、Boolean()
,但是被括号内的条件最终会被转化为布尔值来判断为true
还是false
- 三元运算符号
// 先把 condition 转化为布尔值,为真则执行 '?' 后面的语句,否则执行 ':' 后面的语句
let a = condition ? 'is true' : 'is false';
三元运算其实与if (condition) { /* do something */ } else { /* do something */ }
本质在是一样的,只不过是书写形式上的差异。但是需要如果有下面的例子,则需要尽量避免(容易导致代码比较啰嗦
)。
let a, b, value;
// if else 形式
if (condition) {
a = true;
} else {
a = false;
}
// 三元运算符形式
b = condition ? true : false;
// 以上两者方式都可以简写为下面的形式,避免弄得代码啰嗦
value = !!condition;
- 其余常见的转化为布尔值的情况
let arr = [1, 2, 3];
/* for循环的第二个判断表达式 */
for (let i = 0, len = arr.length; i < len; i++) { /* */}
/* while ()内的条件判断表达式*/
let i = 0,
len = arr.length;
while (i < len) {
let item = arr[i];
console.log(i, item);
i++;
}
/* do while, while括号内的条件表达式 */
do {
let item = arr[i];
console.log(i, item)
i++;
} while (i < len);
宽松相等 == 与 严格相等 ===
首先需要了解的是,==
与===
的区别。在以往对JS的认识中,以及一些博客文章中经常会看到这样的结论:“== 符号
检查是否相等,而=== 符号
会检查值以及类型是否相等”。
看一个🌰
var a =1,
b = '1';
var isEqual = a == b;
var hasEqual = a === b;
易得出结果是: isEqual为true
、hasEqual为false
,所以“==
检查是否相等”这句话肯定是有问题的或者说表述的不够恰当与完整。
:::warning
1 == '1'
一个数字和一个字符串之所以能相等(==),是因为**_==_**
在比较时,a和b为不同类型的两个值,字符串**_b_**
发生了类型转变,转变成了一个数字。而在**_===_**
进行比较时,不同类型的值比较不会发生类型转变。总结就是:**_==_**
在进行比较的时候,会把其中的一个或是两个值强制转化为基本类型,再进行比较。
:::
以下是使用宽松相等 ==
比较常见的一些情况:
- 字符串与数字直接
x == y
如果是x
与y
,一个是数字、另一个是字符串,则需要把字符串转化为数字,然后再进行类类型值的比较。
- 布尔值与其它值
看下面考察JS基础的面试题坑
var x = '2022', y = true, z = false;
var isEuqal = x == y;
var hasEqual = x == z;
// 不在浏览器控制台运行代码,判断isEqual的值,hasEqual的值,以及判断的理由
不要在控制台运行以上代码,推测一下结果。isEqual的值为false
、hasEqual的值为false
;是否感觉有点不符合逻辑,毕竟一个值如果不是真值,那必定是假值。剖析上面的代码:
最重要的一个前提:
==
进行两个不同类型的值进行比较的时候,如果其中一个为布尔值 ,布尔值会被强制转化为Number。 所有!!x 或是(Boolean(x))
的结果不是真值就是假值,这个判断肯定是正确的。但是上面代码中的字符串x
与y
、x
与z
最终的比较得出结果的为false
,这是因为布尔值(y、z)被强制转化成数字,即是字符串x
与数字进行比较==
,最后按着上文字符串与数字进行==比较的方式处理。
注: 如果进行比较相等比较的两个值,其中有一个为布尔值,则一定一定一定不能使用 ==
,而需要用 ===
代替。
null
与undefined
null
与undefined
首先要记住一个点,在使用==
进行比较的时候null
等同与undefined
,即是null == undefined
为真,且null
与undefined
都与自身相等,且不等于(!=)其他类型的值。
下面判断一些常见类型的例子
null == undefined // true
null == null; // true
undefined == undefined; // true
undefined == ''; // false
undefined == false; // false
undefined == 0; // false
null == ''; // false
null == false; // false
null == 0; // flase
其他类型的值与进行 undefined
或是null
进行 ==
比较的时候,不要凭直觉去推断该怎么进行值的类型转化,而需要记住这个特例**_null_**
与**_undefined_**
都与自身相等==,且互相相等==,且不等于(!=)其他类型的值。
见识一下==
让人迷惑的一些代码示例。
var bool1 = '0' == false;
var bool2 = 0 == false;
var bool3 = false == '';
var bool4 = false == [];
var bool5 = '' == [];
var bool6 = '' == 0;
var bool6 = 0 == [];
一一来解释上面比较所的结果值,并及剖析原因:
bool1
的值为true
,==
两边其中一个为布尔值,则布尔值false
转化为Number0
,然后在 数字与字符串之间进行比较==
;bool2
的值为true
,==
两边其中一个为布尔值,则布尔值false
转化为Number0
,然后在 数字与同类型之间进行比较==
;bool3
的值为true
,==
两边其中一个为布尔值,则布尔值false
转化为Number0
,然后在 数字0
与字符串0
之间进行比较==
;bool4
的值为true
,==
两边其中一个为布尔值,则布尔值false
转化为Number0
,空数组[]
与数字进行比较时,空数字会调用调用valueOf() 而后再调用toString()
转化为基本类型''
空字符串,然后在 数字0
与空字符串``之间进行比较==
;bool5
的值为true
,空数组[]
与空字符串进行比较时,空数字会调用调用valueOf() 而后再调用toString()
转化为基本类型''
空字符串,然后字符串之间进行比较==
;bool6
的值为true
,空字符串与数字0
进行比较,首先空字符串会转化为数字0
,然后数字之间进行比较==
;bool7
的值为true
,空数组[]
与数字0
进行比较时,空数字会调用调用valueOf().toString()
转化为基本类型''
空字符串,然后字符串与数字0之间进行比较==
;
转载自:https://juejin.cn/post/7280746697847259199