作者:valentinogagliardi
译者:前端小智
来源:github
阿里云最近在做活动,低至2折,有兴趣可以看看:
https://promotion.aliyun.com/...
为了保证的可读性,本文采用意译而非直译。
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
null
和 undefined
看起来很相似,但它们是两个截然不同的类型,以至于开发人员仍不确定要使用哪个类型。
可以使用 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 内置对象的一部分,它反映了一些基本类型:String
、Number
、Boolean
和Object
。这些内置组件可用于类型之间的转换。转换后,咱们可以拼接两个变量
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 的构建块:String
、Number
、Boolean
、Null、Undefined
、Object
和Symbol
。它们都是大写的,这种风格甚至出现在ECMAScript规范中。但是除了这些基本类型之外,还有一些镜像原语的双胞胎:内置对象。例如,String
类型有一个等效的 String
,它以两种方式使用。如果像函数那样调用(通过传递参数),它会将任何值转换成字符串:
var someValue = 555;
String(someValue);
"555"
如果使用 String
作为 new
的构造函数,那么结果就是String
类型的对象
var someValue = 555;
var newString = new String(someValue);
这种方式值得在控制台中查看对象,看看它与“普通”字符串有何不同
可以使用 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 };
当需要转换某些内容时,可以像使用函数一样使用 Boolean
、String
和 Number
,。
总结
JS 中有七个构建块,即 String
、Number
、Boolean
、Null
、Undefined
、Object
和Symbol
,这些也称为基本类型。
JS 开发人员可以使用算术和比较操作符操作这些类型。但是咱们需要特别注意加法运算符+
和抽象比较运算符 ==
,它们本质上倾向于在类型之间进行转换。
这种 JS 中的隐式转换称为类型强制转换,并在ECMAScript规范中定义。建议在代码中始终使用严格的比较操作符===
代替 ==
。
作为一种最佳实践,当你打算在一种类型和另一种类型之间进行转换时,一定要弄清楚彼此之间的类型。为此,JS 有一堆内建对象,它们基本类型的一种映射:String
、Number
、Boolean
。这些内置函数可用于显式地在不同类型之间进行转换。
思考题:
-
44 - "alex"
的输出结果是? -
44 + "alex"
的输出结果是?为啥? -
"alex" + { name: "Alex" }
的输出结果是 ? - ["alex"] == "alex" 输出结果是啥?为什么呢?
-
undefined == null
输出结果是啥?为什么呢?
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:https://github.com/valentinog...
交流(欢迎加入群,群工作日都会发红包,互动讨论技术)
阿里云最近在做活动,低至2折,有兴趣可以看看:https://promotion.aliyun.com/...
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
https://github.com/qq44924588...
因为篇幅的限制,今天的分享只到这里。如果大家想了解更多的内容的话,可以去扫一扫每篇文章最下面的二维码,然后关注咱们的微信公众号,了解更多的资讯和有价值的内容。
每次整理文章,一般都到2点才睡觉,一周4次左右,挺苦的,还望支持,给点鼓励
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。