转换类型的那些事儿

简介: 转换类型的那些事儿

转换类型的那些事儿


最近一直在看《你不知道的 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');
}

== 和 ===

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

目录
相关文章
|
负载均衡 Dubbo 应用服务中间件
微服务技术系列教程(31) - Dubbo-原理及负载均衡分析
微服务技术系列教程(31) - Dubbo-原理及负载均衡分析
230 0
|
6月前
|
SQL JSON 前端开发
较为完整的SpringBoot项目结构
本文介绍了SpringBoot项目的分层结构与目录组成。项目分为四层:**controller层**(前端交互)、**service层**(业务逻辑处理)、**dao层**(数据库操作)和**model层**(实体类定义)。分层设计旨在实现关注点分离,降低耦合度,提高系统灵活性、可维护性和扩展性。此外,还详细说明了项目目录结构,包括`controller`、`service`、`dao`、`entity`、`param`、`util`等子目录的功能划分,便于团队协作开发。此架构有助于前后端分离,明确各模块职责,符合高内聚低耦合的设计原则。
4116 1
|
JavaScript 编译器 数据安全/隐私保护
TypeScript :关键字
本文介绍了 TypeScript 中的一些核心类型和工具类型,包括 `interface` 和 `type` 的基本使用和区别,以及一些高级类型如 `keyof`、`Record`、`Pick`、`Partial`、`Readonly` 和 `Omit` 的使用方法。文章还详细解释了 `namespace` 的作用和使用场景,帮助开发者更好地组织和管理代码,避免命名冲突,并提高代码的可维护性和可读性。
237 1
|
JavaScript 小程序 Java
二手手机管理系统|基于Springboot的二手手机管理系统设计与实现(源码+数据库+文档)
二手手机管理系统|基于Springboot的二手手机管理系统设计与实现(源码+数据库+文档)
376 1
|
人工智能 前端开发 API
AI智能体研发之路-工程篇(五):大模型推理服务框架LocalAI一键部署
AI智能体研发之路-工程篇(五):大模型推理服务框架LocalAI一键部署
1143 0
|
设计模式 算法 搜索推荐
策略模式的小记
本文介绍了策略模式的概念、结构和使用场景,并通过支付系统的例子展示了如何使用策略模式来动态选择不同的支付策略,包括定义支付策略接口、实现具体的支付策略类、创建上下文类以及在客户端动态选择支付策略。
策略模式的小记
|
算法 安全 网络安全
客户端如何验证ssl/tls证书的合法性
客户端是如何验证ssl/tls证书的合法性
1175 1
|
算法 搜索推荐 编译器
一文带你学透快排(快速排序C语言版)
一文带你学透快排(快速排序C语言版)
|
关系型数据库 MySQL Linux
|
存储 SQL NoSQL
表格存储 Tablestore 十年发展总结
这篇文章接下来会先整体介绍下表格存储 Tablestore,之后会分享下在技术层面产品这几年的功能演进、技术架构演进以及稳定性优化相关的工作,以及在业务层面我们定义的核心应用场景和一些典型案例。
67278 7
表格存储 Tablestore 十年发展总结