深入理解JS的类型、值、类型转换

原创 amandakelake 教程 JavaScript 233阅读 2018-03-16 22:38:01 举报

一、七种内置类型和常见引用类型

插个图,来自于《JavaScript语言精髓与编程实践》第三章P184页,后来想想有点多而杂,所以就自己画了些重点内容如上图

二、特殊的null

typeof来检查上述七种类型时,返回的是对应的类型字符串值

但,有一个例外

null是唯一一个用typeof检测会返回object基本类型值(注意‘基本’两字)

具体的原因,当面试官问到,可以这样吹一波

不同的对象在底层都表示为二进制
在JavaScript中二进制前三位为0的话都会被判断为object类型
null的二进制表示全是0,自然前三位也是0
所以 typeof null === “object”

三、引用类型的子类型:typeof [引用类型] === what ?

上面的图中虽然列出了七种引用类型,但是
typeof ‘引用类型’ === ‘object’一定成立吗?

不,还有一种情况:typeof ‘某些引用类型’ === ‘function’

还是先直接看一些测试吧,看下答案跟你预想的是不是一回事?
如果全都胸有成竹,那下面这一小节你可以跳过了

1、引用类型中的函数

先看前三句,原来typeof除了能判断基本类型object之外,还能判断function类型,函数也属于对象

2、引用类型的子类型

Array举例子

Array是个构造函数,所以直接打印出function
但构造出来的Array()却又是另一回事了,构造出来的结果是个数组,自然属于引用类型,所以也就打印出了‘object’

构造函数 Array(..) 不要求必须带 new 关键字。不带时,它会被自动补上。 因此 Array(1,2,3) 和 new Array(1,2,3) 的效果是一样的

3、引用类型中的基本包装类型

Boolean是个构造函数,第一句没问题
Boolean()直接执行,得出了布尔值,所以得到了‘boolean’

而new出来的是个Boolean对象,具体来说就是:通过构造函数创建出来的是封装了基本类型值的封装对象,好好理解一下这句话

这里用String来举个例子吧,看到了吗,一个封装对象

但是,这里不推荐使用这种封装对象,看个例子

a是个对象,对象永远是真,所以……你懂了

个人建议不要轻易去碰包装类型,日常开发直接用字面量就好了(大牛自动忽略这段话)

4、Math到底是什么?

Math和Global(浏览器中替代为window)都是内置的对象,并不是引用类型的一种

不是函数,不是构造器,这个应该能理解了吧。

四、typeof的安全防范机制

首先,我们需要知道underfinedundeclared的区别
未定义与未声明

但是,对于typeof来说,这两者都一样,返回的都是underfined

很明显,我们知道b就是undeclared(未声明的),但在typeof看来都是一样

这个特性,可以拿来做些什么呢?

举个简单的例子,在程序中使用全局变量 DEBUG 作为“调试模式”的开关。在输出调试信 息到控制台之前,我们会检查 DEBUG 变量是否已被声明。顶层的全局变量声明 var DEBUG = true 只在 debug.js 文件中才有,而该文件只在开发和测试时才被加载到浏览器,在生产环 境中不予加载。

问题是如何在程序中检查全局变量 DEBUG 才不会出现 ReferenceError 错误。这时 typeof 的 安全防范机制就成了我们的好帮手:

这不仅对用户定义的变量(比如 DEBUG)有用,对内建的 API 也有帮助:

这样的安全防范机制在各式源码中非常常见,可见,大作们早已经把一些基础的东西弄得非常透彻并运用到实践中,所以说,看源码是我们快速提高的一个方式,应该错不了。

五、值

这一part引用自一、内存空间详解 · Sample GitBook

JS的执行上下文生成之后,会创建一个叫做变量对象的特殊对象(关于变量对象在我的其他文章中有讲到),JS的基础类型都保存在变量对象中

严格意义上来说,变量对象也是存放于堆内存中,但是由于变量对象的特殊职能,我们在理解时仍然需要将其于堆内存区分开来。

但引用数据类型的值是保存在堆内存中的对象。JavaScript不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。
在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。
这里的引用,我们可以理解为保存在变量对象中的一个地址,该地址与堆内存的实际值相关联。

看到这里,应该就能比较好的理解引用传参的相关问题了,这属于延伸思考,google去吧,学会自我思考和搜索也是一种技能。

六、强制类型转换

《you don’t know JS》中 第一部分第4章

类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时(runtime)。

然而在 JavaScript 中通常将它们统称为强制类型转换,我个人则倾向于用“隐式强制类型转换”(implicit coercion)和“显式强制类型转换”(explicit coercion)来区分。

1、抽象值操作

介绍显式和隐式强制类型转换之前,我们需要先掌握字符串、数字和布尔值之间类型转换的基本规则

1️⃣ToString
toString() 可以被显式调用,或者在需要字符串化时自动调用

null 转换为 "null",undefined 转换为 "undefined",true 转换为 "true"。
数字的字符串化则遵循通用规则
极小和极大的 数字使用指数形式:

数组的默认 toString() 方法经过了重新定义,将所有单元字符串化以后再用 "," 连接起 来

2️⃣ ToNumber
其中 true 转换为 1,false 转换为 0。undefined 转换为 NaN,null 转换为 0。
处理失败 时返回 NaN(处理数字常量失败时会产生语法错误)

3️⃣ ToBoolean
先看什么是假值

假值的布尔强制类型转换结果为 false。
从逻辑上说,假值列表以外的都应该是真值(truthy)

再看下假值对象(这东西太有意思了😂)
不是说规定所有的对象都是真值,怎么还会有假值对象呢?

看看d1和d2有什么不同?是不是特有意思?

如果假值对象并非封装了假值的对象,那它究竟是什么?
值得注意的是,虽然 JavaScript 代码中会出现假值对象,但它实际上并不属于 JavaScript 语 言的范畴。
浏览器在某些特定情况下,在常规 JavaScript 语法基础上自己创建了一些外来(exotic) 值,这些就是“假值对象”。
假值对象看起来和普通对象并无二致(都有属性,等等),但将它们强制类型转换为布尔 值时结果为 false。

最后再看真值是什么
真值就是假值列表之外的值
再来看一段有意思的代码

到目前为止,我们得出的一个结论是:[]、{} 和 function(){} 都不在假值列表中,因此它们都 是真值

看几个常用的吧

那是不是记住假值,就知道哪些是真值了?
理论上是的……
那实际上是什么?
真正掌握类型转换!

2、显式类型转换

这个其实很好理解

3、隐式强制类型转换

1️⃣字符串和数字之间的隐式转换
多的不谈了,简单来说就是,如果 + 的其中一个操作数是字符串(或者通过以上步骤可以得到字符串), 则执行字符串拼接;否则执行数字加法。

有个小坑,可以当做程序员饭后趣谈

《you don’t know JS 》中5.1.3章节是这样说的

还有一个坑常被提到(涉及强制类型转换,参见第 4 章)
[] + {}; // "[object Object]"
{} + []; // 0
表面上看 + 运算符根据第一个操作数([] 或 {})的不同会产生不同的结果,实则不然。 第一行代码中,{} 出现在 + 运算符表达式中,因此它被当作一个值(空对象)来处理。第4 章讲过 [] 会被强制类型转换为 "",而 {} 会被强制类型转换为 "[object Object]"。
但在第二行代码中,{} 被当作一个独立的空代码块(不执行任何操作)。代码块结尾不需 要分号,所以这里不存在语法上的问题。最后 + [] 将 [] 显式强制类型转换(参见第 4 章) 为 0。

但目前的chrome浏览器控制台是这样的

对此,你怎么看?😏

{} 其实应该当成一个代码块,而不是一个 Object,当你在console.log使用的时候,{} 被当成了一个 Object

这下是不是印象更深刻了?

2️⃣ 隐式强制类型转换为布尔值
下面的情况会发生 布尔值隐式强制类型转换。

  • (1)if (..)语句中的条件判断表达式。
  • (2)for ( .. ; .. ; .. )语句中的条件判断表达式(第二个)。
  • (3) while (..) 和 do..while(..) 循环中的条件判断表达式。
  • (4)? :中的条件判断表达式。
  • (5) 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)。

3️⃣ || 与 &&
就一句话,理解了就万岁,称之为“操作数选择器”

只选择其中一个

4、== 与 ===

常见的误区是“== 检查值是否相等,=== 检查值和类型是否相等”
正确的解释是:“== 允许在相等比较中进行强制类型转换,而 === 不允许。”

两个完全截然不同的理解方向,果然,看书还是要看权威的书好

这一段,看完后我只想总结一句,放弃 == ,拥抱 ===,其他的不谈了

我在学习过程中喜欢做记录,分享的是自己在前端之路上的一些积累和思考,希望能跟大家一起交流与进步。 这是我的github博客,欢迎star

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复