1. 首页

【JS 小书】第 7 章:JS 中的类型转换与比较

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);

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

码农进阶题库,每天一道面试题 or Js小知识

可以使用 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 输出结果是啥?为什么呢?

*

码农进阶题库,每天一道面试题 or Js小知识 https://www.javascriptc.com/interview-tips/

往期推荐

作者:valentinogagliardi
译者:前端小智
链接:https://segmentfault.com/a/1190000020531740

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:【JS 小书】第 7 章:JS 中的类型转换与比较

链接:https://www.javascriptc.com/3387.html

« 将前端技术栈移植到掌上游戏机
吃透移动端 H5 与 Hybrid|实践踩坑12种问题汇总»
Flutter 中文教程资源

相关推荐

QR code