转换类型的那些事儿

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 转换类型的那些事儿

转换类型的那些事儿


最近一直在看《你不知道的 JS》,轻总结下,转换类型。

强制转换分为隐式和显式转化,虽然名声不好,但还是有必要知道转换的规则是啥。

显式的强制转换,通过减少困惑,增强了代码的可读性和可维护性。

隐式的强制转换,有其的“隐藏的”副作用,但有些也是为了增强代码的可读性。

TL;DR

  • ToString:基本类型和函数就是原地加上引号。但对象、数组转化就麻烦点。
  • JSON.stringify:不识别undefined和函数,对于对象可以跳过部分值。可以有第二个和第三个参数。
  • ToNumber:一句话难概括。。。
  • ToBoolean:只有特定的值是可以转化为false,其他都是true
  • parseInt:是从字符串里解析数字,遇到非数字就停止。要始终传字符串类型,且加上进制参数
  • 隐式转化:各种条件表达式、加减乘除、><、==。><尽量转化为数字之后在使用。尽量用===替换==

ToString()

一般用xx.toString(xx)或者String(xx)显示转化。

基本类型有自然的字符串化形式

String(null); // 'null'
String(undefined); // 'undefined'
String(true); // 'true'
String(1); // '1'
String(1000000000000000000000.1); // "1e+21", 稍微注意的是,如果数很大或者很小,会变成指数的
  • 普通对象,会调用默认的toString(),会返回内部的[[ Class ]]
  • 函数,会返回函数的字符串形式
  • 数组,会将数组每个都字符串化,然后用 **","**拼接每个字符串
String({}); // "[object Object]"(
String(function () {}); // "function(){}"
String([1, 2, {}]); // "1,2,[object Object]"

JSON.stringify(xx)

stringnumberboolean、和 null 值在 JSON 字符串化时,和xx.toString()的值基本是相同的。

JSON.stringify(null); // 'null'
JSON.stringify(true); // 'true'
JSON.stringify(1); // '1',
JSON.stringify(1000000000000000000000.1); // "1e+21",稍微注意的是,如果数很大或者很小,会变成指数的

但是,其他类型的都不一样了!!!

  • 遇到 undefinedfunction、和 symbol 时将会自动地忽略它们,返回undefined
  • 遇到array的时候,如果某项是上面的值,会替换成null
  • 遇到对象的时候,如果对象有**toJSON**的方法,会将其返回值字符串化。没有的话,直接转化,会跳过值是undefinedfunction、和 symbol
JSON.stringify(undefined); // undefined
JSON.stringify(function () {}); // undefined
JSON.stringify([1, 2, undefined, function () {}]); // "[1,2,null,null]"
JSON.stringify({ a: 1, b: 2, c: undefined, hello() {} }); // "{\"a\":1,\"b\":2}"
JSON.stringify({
  a: 1,
  b: 2,
  toJSON() {
    return { a: this.a };
  },
}); // "{\"a\":1}"

JSON.stringify 可以不止一个参数

第二个参数和第三个参数都是可选的。

第二个参数的作用类似toJSON,可以指定特定的key字符串化,可以是数组,也可以是函数。 第三个参数的作用,就是每级缩进,个人觉得这个,作用不大,知道就行。

var a = { b: 1, c: 2 };
JSON.stringify(a, ['b']); // "{\"b\":1}"
JSON.stringify(a, function (k, v) {
  if (k === 'b') return v;
}); // "{\"b\":1}"
JSON.stringify(a, ['c'], '--'); // "{\n--\"c\": 2\n}"

网络异常,图片无法展示
|

ToNumber

一般用Number(xx)显示转化。

  • undefined的话,变成NaN
  • null的话,变成0
  • Boolean的话true变成1false变成0
  • 字符串的话,含整数或者小数都可以变成正常数字,其他都是NaN
Number(undefined); // NaN
Number(null); // 0
Number(true); // 1
Number(false); // 0
Number('1.2'); // 1.2
Number('1.2d'); // NaN

对象以及数组,会先转换成基本类型值的等价物,然后根据上面的规则继续转化。

转换成等价物说白了,就是先调用xx.valueOf()没有这个方法的话,就调用xx.toString(),如果这两方法返回基本类型值的话,则Number(返回值),没返回基本值的话就报错

var a = {
  valueOf() {
    return '1';
  },
  toString() {
    return '2';
  },
};
Number(a); // 1
var b = { c: 1 };
Number(b); // NaN
var c = {
  c: 1,
  toString() {
    return {};
  },
};
Number(c); // 报错

ToBoolean

一般用Boolean(xx)显示转化。

将其他类型的值转化为 Boolean 类型的值,其实只要记住哪些转化为false即可,其他的都是true

  • undefined
  • null
  • false
  • +0, -0, NaN
  • ""

特别注意:

  • 空对象、空函数、空数组转换成 Boolean 的话都是true
  • 用 new 创建基本类型的时候,此时转化成 Boolean 的话也是true
Boolean({} && [] && function () {}); // true
Boolean(new String('') && new Number(0) && new Boolean(false)); // true

其他的显式转换

上面的String(x)/Number(x)/Boolean(x)都是显式转化。

还有一些其他公认的显式转换:

// number => string
1 + '' === String(1);
// string => number
+'1' === Number('1');
// date => number  获取当前时间戳 等同于 Date.now() 或 new Date().getTime()
+new Date() === Number(new Date());
// 任意类型 => Boolean
!!1;

~x 等同于 -(x+1)

有时候,还会看到~。 在 JS 里,常和indexOf结合使用。

-1也是另一种哨兵值,C 语言里-1表示失败。 而 JS 里的indexOf返回值为-1 的话,也表示没找到。

const isInclude = 'abc'.indexOf('d') !== -1;
if (isInclude) {
  // ...
}

这种写法没毛病,但如果用~更简洁,这时候用在 if 里,相当于转换成 Boolean

const isInclude = ~('abc').indexOf('d')
if (isInclude).indexOf('d')) {
  // ...
}

解析数字字符串:和强制转换不一样

解析数字字符串:从一个字符串中解析出一个数字是 容忍 非数字字符的 —— 从左到右,如果遇到非数字字符就停止解析 。 强制转换是 不容忍 并且会失败而得出值 NaN。

解析不应当被视为强制转换的替代品

var a = '42';
var b = '42px';
Number(a); // 42
parseInt(a); // 42
Number(b); // NaN
parseInt(b); // 42

parseInt 是工作在string值上的,所以永远不要传入非 string,不然会强制转换成 string。

parseInt 还有第二个可选参数,表示将字符串翻译成几进制(2-35),默认是 10 进制,总是在第二个参数值上传递进制,注意,最终返回的结果是 10 进制。

parseInt('0x16', 10); // 0
parseInt('0x16', 16); // 22
parseInt('15', 10); // 15
// 15作为16进制的翻译,转化为10进制就是21
parseInt('15', 16); // 21

参数是非字符串的话,会有各种奇怪的现象,所以永远不要传入非 string

parseInt(1 / 0, 19); // 18  ("I" from "Infinity")
parseInt(0.000008); // 0   ("0" from "0.000008")
parseInt(0.0000008); // 8   ("8" from "8e-7")
parseInt(false, 16); // 250 ("fa" from "false")
parseInt(parseInt, 16); // 15  ("f" from "function..")
parseInt('0x10'); // 16
parseInt('103', 2); // 2

其他的隐式转换

- 、* 、/ 运算符会将两边隐式变成 number 类型

这个还好,也容易理解。

'1' - '2' === -1;

+ 运算符

使用加号运算符要小心,加到字符串类型的时候,另一边会强制转化成字符串类型。

2 + 1 + '1' === '31';

> 和 <

这种也会发生类型转换,为了消除歧义,最好转化为数字类型之后在使用。

var a = [42];
var b = '043';
a < b; // false -- 字符串比较!
Number(a) < Number(b); // true -- 数字比较!
'1' - '2';

* -> Boolean

哪个种类的表达式操作(隐含地)要求/强制一个 boolean 转换呢?

  • 在一个if (..)语句中的测试表达式。
  • 在一个for ( .. ; .. ; .. )头部的测试表达式(第二个子句)。
  • while (..)do..while(..)循环中的测试表达式。
  • ? :三元表达式中的测试表达式(第一个子句)。
  • ||(“逻辑或”)和&&(“逻辑与”)操作符左手边的操作数(它用作测试表达式 —— 见下面的讨论!)

特别注意:一个&&或||操作符产生的值不见得是 Boolean 类型。这个产生的值将总是两个操作数表达式其中之一的值!

var a = 42;
var b = 'abc';
var c = null;
a || b; // 42
a && b; // "abc"
c || b; // "abc"
c && b; // null
// 经常可能会写这样的代码
var a = 42;
var b = null;
var c = 'foo';
// 其实这里相当于 if("foo") 因为是if表达式,所以隐式转化为 if(true)
if (a && (b || c)) {
  console.log('yep');
}

== 和 ===

==两边的转换规则,可能超乎你的想象,所以,最好直接使用===

目录
相关文章
|
6月前
|
传感器 人工智能 图形学
UnityAI——常用感知类型的实现
UnityAI——常用感知类型的实现
|
1月前
3D转换
【10月更文挑战第6天】3D转换。
29 4
|
1月前
2D转换
【10月更文挑战第6天】2D转换。
34 3
|
程序员 数据库
软件文档的类型有哪些?
软件文档的类型有哪些?
212 0
|
6月前
|
编译器 C++
47不同类型数据间的转换
47不同类型数据间的转换
33 0
|
存储 Python
什么是数据类型,都有什么,如何转换
什么是数据类型,都有什么,如何转换
146 0
C#之里氏转换
C#之里氏转换
94 0
|
JavaScript 前端开发
比较不同的类型
比较不同的类型
94 0