9月阅读周·JavaScript权威指南:类型、值和变量,类型转换篇

举报
叶一一 发表于 2024/09/22 11:25:14 2024/09/22
【摘要】 背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效,已经坚持阅读八个月。已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScri...

背景

去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。

没有计划的阅读,收效甚微。

新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。

这个“玩法”虽然常见且板正,但是有效,已经坚持阅读八个月。

已读完书籍《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》

当前阅读周书籍《JavaScript权威指南》

类型转换

如果JavaScript期望使用一个数字,它把给定的值将转换为数字(如果转换结果无意义的话将返回NaN),一些例子如下:

  10+"objects"//=>"10 objects".数字10转换成字符串
  "7"*"4"//=>28:两个字符串均转换为数字
  var n=1-"x";//=>NaN:字符串"x"无法转换为数字
  n+"objects"//=>"NaN objects":NaN转换为字符串"NaN"

原始值到对象的转换也非常简单,原始值通过调用String()、Number()或Boolean()构造函数,转换为它们各自的包装对象。

null和undefined属于例外,当将它们用在期望是一个对象的地方都会造成一个类型错误(TypeError)异常,而不会执行正常的转换。

转换和相等性

由于JavaScript可以做灵活的类型转换,因此其“==”相等运算符也随相等的含义灵活多变。例如,如下这些比较结果均是true:

  null==undefined//这两值被认为相等
  "0"==0//在比较之前字符串转换成数字
  0==false//在比较之前布尔值转换成数字
  "0"==false//在比较之前字符串和布尔值都转换成数字

需要特别注意的是,一个值转换为另一个值并不意味着两个值相等。比如,如果在期望使用布尔值的地方使用了undefined,它将会转换为false,但这并不表明undefined==false。JavaScript运算符和语句期望使用多样化的数据类型,并可以相互转换。if语句将undefined转换为false,但“==”运算符从不试图将其操作数转换为布尔值。

显式类型转换

尽管JavaScript可以自动做许多类型转换,但有时仍需要做显式转换,或者为了使代码变得清晰易读而做显式转换。

做显式类型转换最简单的方法就是使用Boolean()、Number()、String()或Object()函数。当不通过new运算符调用这些函数时,它们会作为类型转换函数并按照规则做类型转换:

  Number("3")//=>3
  String(false)//=>"false"或使用false.toString()
  Boolean([])//=>true
  Object(3)//=>new Number(3)

需要注意的是,除了null或undefined之外的任何值都具有toString()方法,这个方法的执行结果通常和String()方法的返回结果一致。同样需要注意的是,如果试图把null或undefined转换为对象则会抛出一个类型错误(TypeError)。Object()函数在这种情况下不会抛出异常:它仅简单地返回一个新创建的空对象。

JavaScript中的某些运算符会做隐式的类型转换,有时用于类型转换。如果“+”运算符的一个操作数是字符串,它将会把另外一个操作数转换为字符串。一元“+”运算符将其操作数转换为数字。同样,一元“!”运算符将其操作数转换为布尔值并取反。在代码中会经常见到这种类型转换的惯用法:

  x+""//等价于String(x)
  +x//等价于Number(x).也可以写成x-0
  !!x//等价于Boolean(x).注意是双叹号

在计算机程序中数字的解析和格式化是非常普通的工作,JavaScript中提供了专门的函数和方法用来做更加精确的数字到字符串(number-to-string)和字符串到数字(string-to-number)的转换。

Number类定义的toString()方法可以接收表示转换基数(radix)的可选参数,如果不指定此参数,转换规则将是基于十进制。同样,亦可以将数字转换为其他进制数(范围在2~36之间),例如:

  var n=17;
  binary_string=n.toString(2);//转换为"10001"
  octal_string="0"+n.toString(8);//转换为"021"
  hex_string="0x"+n.toString(16);//转换为"0x11"

当处理财务或科学数据的时候,在做数字到字符串的转换过程中,你期望自己控制输出中小数点位置和有效数字位数,或者决定是否需要指数记数法。Number类为这种数字到字符串的类型转换场景定义了三个方法。toFixed()根据小数点后的指定位数将数字转换为字符串,它从不使用指数记数法。toExponential()使用指数记数法将数字转换为指数形式的字符串,其中小数点前只有一位,小数点后的位数则由参数指定(也就是说有效数字位数比指定的位数要多一位),toPrecision()根据指定的有效数字位数将数字转换成字符串。如果有效数字的位数少于数字整数部分的位数,则转换成指数形式。我们注意到,所有三个方法都会适当地进行四舍五入或填充0。看一下下面几个例子:

  var n=123456.789;
  n.toFixed(0);//"123457"
  n.toFixed(2);//"123456.79"
  n.toFixed(5);//"123456.78900"
  n.toExponential(1);//"1.2e+5"
  n.toExponential(3);//"1.235e+5"
  n.toPrecision(4);//"1.235e+5"
  n.toPrecision(7);//"123456.8"
  n.toPrecision(10);//"123456.7890"

如果通过Number()转换函数传入一个字符串,它会试图将其转换为一个整数或浮点数直接量,这个方法只能基于十进制数进行转换,并且不能出现非法的尾随字符。parseInt()函数和parseFloat()函数(它们是全局函数,不从属于任何类的方法)更加灵活。parseInt()只解析整数,而parseFloat()则可以解析整数和浮点数。如果字符串前缀是"0x"或者"0X",parseInt()将其解释为十六进制数,parseInt()和parseFloat()都会跳过任意数量的前导空格,尽可能解析更多数值字符,并忽略后面的内容。如果第一个非空格字符是非法的数字直接量,将最终返回NaN:

  parseInt("3 blind mice")//=>3
  parseFloat("3.14 meters")//=>3.14
  parseInt("-12.34")//=>-12
  parseInt("0xFF")//=>255
  parseInt("0xff")//=>255
  parseInt("-0XFF")//=>-255
  parseFloat(".1")//=>0.1
  parseInt("0.1")//=>0
  parseInt(".1")//=>NaN:整数不能以"."开始
  parseFloat("$72.47");//=>NaN:数字不能以"$"开始

parseInt()可以接收第二个可选参数,这个参数指定数字转换的基数,合法的取值范围是2~36,例如:

  parseInt("11",2);//=>3(1*2+1)
  parseInt("ff",16);//=>255(15*16+15)
  parseInt("zz",36);//=>1295(35*36+35)
  parseInt("077",8);//=>63(7*8+7)
  parseInt("077",10);//=>77(7*10+7)

对象转换为原始值

对象到布尔值的转换非常简单:所有的对象(包括数组和函数)都转换为true。对于包装对象亦是如此:new Boolean(false)是一个对象而不是原始值,它将转换为true。

对象到字符串(object-to-string)和对象到数字(object-to-number)的转换是通过调用待转换对象的一个方法来完成的。一个麻烦的事实是,JavaScript对象有两个不同的方法来执行转换,并且接下来要讨论的一些特殊场景更加复杂。值得注意的是,这里提到的字符串和数字的转换规则只适用于本地对象(native object)。宿主对象(例如,由Web浏览器定义的对象)根据各自的算法可以转换成字符串和数字。

所有的对象继承了两个转换方法。第一个是toString(),它的作用是返回一个反映这个对象的字符串。默认的toString()方法并不会返回一个有趣的值:

  ({x:1,y:2}).toString()//=>"[object Object]"

很多类定义了更多特定版本的toString()方法。例如,数组类(Array class)的toString()方法将每个数组元素转换为一个字符串,并在元素之间添加逗号后合并成结果字符串。函数类(Function class)的toString()方法返回这个函数的实现定义的表示方式。实际上,这里的实现方式是通常是将用户定义的函数转换为JavaScript源代码字符串。日期类(Date class)定义的toString()方法返回了一个可读的(可被JavaScript解析的[插图])日期和时间字符串。RegExp类(RegExp class)定义的toString()方法将RegExp对象转换为表示正则表达式直接量的字符串:

  [1,2,3].toString()//=>"1,2,3"
  (function(x){f(x);}).toString()//=>"function(x){\n f(x);\n}"
  /\d+/g.toString()
  //=>"/\\d+/g"
  new Date(2010,0,1).toString()//=>"Fri Jan 01 2010 00:00:00 GMT-0800(PST)"

另一个转换对象的函数是valueOf()。这个方法的任务并未详细定义:如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值,因此默认的valueOf()方法简单地返回对象本身,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个默认方法,调用这些类型的实例的valueOf()方法只是简单返回对象本身。日期类定义的valueOf()方法会返回它的一个内部表示:1970年1月1日以来的毫秒数。

  var d=new Date(2010,0,1);//2010年1月1日(太平洋时间)
  d.valueOf()//=>1262332800000

总结

JavaScript中对象到字符串的转换经过了如下这些步骤:

  • 如果对象具有toString()方法,则调用这个方法。如果它返回一个原始值,JavaScript将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。
  • 如果对象没有toString()方法,或者这个方法并不返回一个原始值,那么JavaScript会调用valueOf()方法。如果存在这个方法,则JavaScript调用它。如果返回值是原始值,JavaScript将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。
  • 否则,JavaScript无法从toString()或valueOf()获得一个原始值,因此这时它将抛出一个类型错误异常。

在对象到数字的转换过程中,JavaScript做了同样的事情,只是它会首先尝试使用valueOf()方法:

  • 如果对象具有valueOf()方法,后者返回一个原始值,则JavaScript将这个原始值转换为数字(如果需要的话)并返回这个数字。
  • 否则,如果对象具有toString()方法,后者返回一个原始值,则JavaScript将其转换并返回。
  • 否则,JavaScript抛出一个类型错误异常。

作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏️ | 留言📝

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。