js string转number_【JS 口袋书】第 7 章:JS 中的类型转换与比较

作者:valentinogagliardi
译者:前端小智
来源:github

为了保证的可读性,本文采用意译而非直译。

JS 中的基本类型

JS 有 7 种基本类型,分别如下:

  • String

  • Number

  • Boolean

  • Null

  • Undefined

  • Object

  • Symbol (ES6)

布尔值表示的值可以是true,也可以是false。另一方面,null是故意缺少一个值。null通常被赋值给一个变量,用来表示变量过后会被赋予值。

var maybe = null;

然后是undefined,表示是一个变量没有任何附加项:

var name;
console.log(name)
undefined

nullundefined看起来很相似,但它们是两个截然不同的类型,以至于开发人员仍不确定要使用哪个类型。

可以使用typeof操作符来查看变量的类型:

typeof "alex"
"string"

number类型:

typeof 9
"number"

boolean类型:

typeof false
"boolean"

undefined类型:

typeof undefined
"undefined"

null类型

typeof null
"object"

这个结果有点奇怪。null看起来像一个对象,但实际上它是JS的一个历史错误,自该语言诞生以来就一直存在。由于这些原因,JS 一直名声不佳。null只是其中的一例。另外,一种类型和另一种类型之间的转换有一些奇怪的规则。

先给大家介绍一下背景。各位先用Python做一个例子。Python 中的以下指令

'hello' + 89

这样会得到一个明确的错误:

TypeError: can only concatenate str (not "int") to str

在 JS 中完全没有问题:

'hello' + 89

结果:

"hello89"

更加奇怪是直接加一个数组:

'hello' + []

结果:

'hello'

再来:

'hello' + [89]

结果:

"hello89"

看起来这种转换背后有某种逻辑,甚至还可以有更加复杂的数组结构:

'hello' + [89, 150.156, 'mike']

结果:

"hello89,150.156,mike"

这两行 JS 足以让 Java 开发人员望而却步。但是 JS 中的这种行为是100%故意的。因此,有必要研究一下 JS 中隐式转换(也称为类型强制转换)。

当数字变成字符串

一些编程语言有一个称为类型转换的概念,这意味着:如果咱们想将一个类型转换成另一种类型,那么必须使转换明确。在 JS 中也有提供这种方法。考虑以下示例

var greet = "Hello";
var year = 89;

如果想进行显式转换,可以在代码中用toString()方法:

var greet = "Hello";
var year = 89;

var yearString = year.toString()

或者使用String

var greet = "Hello";
var year = 89;

var yearString = String(year)

String是JS 内置对象的一部分,它反映了一些基本类型:StringNumberBooleanObject。这些内置组件可用于类型之间的转换。转换后,咱们可以拼接两个变量

greet + yearString;

但是除了这种显式转换之外,在 JS 中还有一种微妙的机制,称为隐式转换,由 JS 引擎提供。

'hello' + 89

结果:

"hello89"

但是这种转换背后的逻辑是什么?你可能会惊讶地发现,如果 JS 中的加法运算符+中的一个是字符串,则会自动将两个操作数中的任何一个转换为字符串!

更令人惊讶的是,这个规则在ECMAScript规范中已经固定下来了。第11.6.1节定义了加法运算符的行为,在这里总结一下:

加法运算符(+)
如果 x 是字符串或者 y 是字符串那么返回 ToString(x) 后面跟 ToString(y)

这种把戏只对数字有效吗? 不是,数组和对象一样的,跑不掉:

'hello' + [89, 150.156, 'mike']

结果:

"hello89,150.156,mike"

对象怎样:

'hello' + { name: "Jacopo" }

结果:

"hello[object Object]"

为了弄清,咋肥事,可以通过将对象转换为字符串来进行快速测试:

String({ name: "Jacopo" })

结果:

"[object, Object]"

但还有另一个问题:乘法、除法和减法的情况又是肿么样的?

我不是一个数字!

咱们看到加法运算符在至少一个操作数是字符串时,是如何将操作数转换成字符串。但是其他的算术运算符呢?

操作符描述
+
++自增
*
**指数 (es6)
-
--自减
/
%取余

如果对不是数字的类型使用其中一个操作符(+除外),那么就得到了一种特殊类型的 :NaN

89 ** "alex"

结果:

NaN

NaN表示不是数字,任何失败的算术运算,如下面的代码所示:

var obj = { name: "Jacopo" } % 508897

结果:

console.log(obj)
NaN

注意与NaN结合的typeof。这个代码看起来没问题:

typeof 9 / "alex"
NaN

那下面呢?

var strange = 9 / "alex"

再使用typeof:

typeof strange
"number"

NaN被分配给一个变量时,它就变成了number,这就引出了一个新问题。我如何检查一些变量是否是NaN? ES6 中有一个名为isNaN()的新方法:

var strange = 9 / "alex"
isNaN(strange)
true

接着来看看 JS 中的比较运算符,它们和算术运算符一样奇怪。

相等还是不等

JS 中有两大类比较运算符。首先是所说的“弱比较”。它是一个抽象的比较运算符(双等号):==。然后还有一个“强比较”:===,又名严格比较运算符。他俩兄弟的行为方式不一样,来看看一些例子。

首先,如果咱们用两个操作符比较两个字符串,俩兄弟得到一致的结果:

"hello" == "hello"
true

"hello" === "hello"
true

看起来很 nice,现在来比较两种不同的类型,数字和字符串。

首先是“强比较”

"1" === 1
false

很明显,字符串 1 等于数字 1。弱比较又是怎么样?

"1" == 1
true

true表示这两个值相等。这种行为与咱们前面看到的隐式转换有关。原来,抽象比较操作符会在比较类型之前自动转换类型。这是一个摘要:

抽象等式比较算法
比较x == y是这样执行的:如果x是字符串,y是数字,返回比较的结果ToNumber(x) == y

说白了就是:如果第一个操作数是字符串,第二个操作数是数字,那么将第一个操作数转换为数字。

有趣的是,JS 规范中充满了这些疯狂的规则,我强烈建议对此进行更深入的研究。当然现在都建议使用强比较。

严格相等比较的规范指出,在将值与===进行比较之前,不会进行自动转换。在代码中使用严格的相等比较可以避免愚蠢的错误。

基本数据类型与对象

咱们已经看到了 JS 的构建块:StringNumberBoolean、Null、UndefinedObjectSymbol。它们都是大写的,这种风格甚至出现在ECMAScript规范中。但是除了这些基本类型之外,还有一些镜像原语的双胞胎:内置对象。例如,String类型有一个等效的String,它以两种方式使用。如果像函数那样调用(通过传递参数),它会将任何值转换成字符串:

var someValue = 555;

String(someValue);

"555"

如果使用String作为new的构造函数,那么结果就是String类型的对象

var someValue = 555;

var newString = new String(someValue);

这种方式值得在控制台中查看对象,看看它与“普通”字符串有何不同

b373823b8252dea92c9e6cc2e355eb49.png

可以使用typeof来确认它确实是一个对象:

typeof newString
"object"

基本类型的 Number 也有一个内置对象Number,它可以(几乎)将任何值转换为数字:

var someValue = "555";

Number(someValue);

555;

我说的几乎是因为在试图转换无效的“数字”时得到NaN

var notValidNumber = "aa555";

Number(notValidNumber);

NaN;

在构造函数形式Number中使用时,将返回number类型的新对象:

var someValue = "555";

new Number(someValue);

Number {555}

内置对象Boolean以将任何值转换成布尔值:

var convertMe = 'alex';

Boolean(convertMe)

true

用构造函数的方式会返回一个对象:

var convertMe = 'alex';

typeof new Boolean(convertMe)

"object"

内置的Object行为也一样:

Object('hello'); // String {"hello"}

Object(1); // Number{1}

如果在没有参数的情况下调用,它将返回一个空对象

Object()

{}

如果作为构造函数调用,则返回一个新对象

new Object({
  name: "Alex",
  age: 33
});

{name: "Alex", age: 33}

此时,你可能会问自己:什么时候可以使用内置对象,什么时候应该使用基本类型初始化值? 根据经验,当你只需要一个简单的类型时,应该避免构造函数调用:

// 不要这么用var bool = new Boolean("alex");var str = new String('hi');var num = new Number(33);var strObj = new Object('hello')

除了不实用之外,这种形式还会带来性能损失,因为这种方式每次都会创建一个新对象。最后但同样重要的是,当咱们想要一个简单的字符串或数字时,创建一个对象是没有意义的。所以下面的形式是首选的

// ok 的
var bool = true
var str = 'hi';
var num = 33;
var obj = { name: "Alex", age: 33 };

当需要转换某些内容时,可以像使用函数一样使用BooleanStringNumber,。

总结

JS 中有七个构建块,即StringNumberBooleanNullUndefinedObjectSymbol,这些也称为基本类型。

JS 开发人员可以使用算术和比较操作符操作这些类型。但是咱们需要特别注意加法运算符+和抽象比较运算符==,它们本质上倾向于在类型之间进行转换。

这种 JS 中的隐式转换称为类型强制转换,并在ECMAScript规范中定义。建议在代码中始终使用严格的比较操作符===代替==

作为一种最佳实践,当你打算在一种类型和另一种类型之间进行转换时,一定要弄清楚彼此之间的类型。为此,JS 有一堆内建对象,它们基本类型的一种映射:StringNumberBoolean。这些内置函数可用于显式地在不同类型之间进行转换。

思考题:

  1. 44 - "alex"的输出结果是?

  2. 44 + "alex"的输出结果是?为啥?

  3. "alex" + { name: "Alex" }的输出结果是 ?

  4. ["alex"] == "alex" 输出结果是啥?为什么呢?

  5. undefined == null输出结果是啥?为什么呢?

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具Fundebug。

原文:https://github.com/valentinogagliardi/Little-JavaScript-Book/blob/v1.0.0/manuscript/chapter7.md

交流

227984d1a42b33bbb21570c54d27ed0e.png

延伸阅读

【JS 口袋书】第 1 和 2 章:JS简介及基础

【JS 口袋书】第 3 章:JavaScript 函数

【JS 口袋书】第 4 章:JS 引擎底层的工作原理

【JS 口袋书】第 5 章:JS 中的闭包与模块

【JS 口袋书】第 6 章:JS 对象生命周期的秘密

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值