js算术运算符与数据类型转换

加法运算符/字符串连接符

加法运算符的重载(overload):
(1)字符串连接符:
当"+“号的两边有一个是字符串类型时,”+“为字符串连接符.另一个非字符串类型(对象,number etc.),会先被转换为字符串,然后执行字符串连接操作.
(2)其余情况下,为数值的加号.
(3)作用是"加法运算符"还是"字符串连接符”,这是在运行时判断决定的.

加法运算符的结合性(运算次序):
* 左结合(多个加号同时存在时,从左边依次算到右边)
* 结合性+重载:(举例)
var result = 3+7+‘percent’ //结果:‘10percent’
分析:"+"左结合,因此先计算左边的3+7,结果为10;在计算10+‘percent’,作字符串连接,结果为’10percent’.

对象相加:
(1)基本原则:先将对象转换为基本类型,再进行判断处理(相加或者字符串连接).
(2)对象类型转换为原始类型的规则:

√要点一:对象内部的[[DefaultValue]] (hint)方法

1.这个方法是所有对象都有的内部方法之一.由于是内部方法,我们无法直接调用,而是js在需要(强制类型转换)或者期望(隐式转换)对象类型转换为原始类型值时自动调用.
2.它的主要任务就是将对象类型值转换为对应的原始类型值.
3.参数hint:是一个字符串,其值可以是’Number’,'String’和省略.
4.该方法的实现逻辑:
当hint为’String’时,遵循以下步骤:

  1. 先以无参形式调用对象本身的toString(),并判断该方法返回值.若返回值str为原始类型值,则[[DefaultValue]] (String)返回值为str.
  2. 若str不是原始类型,继续以无参形式调用对象本身的valueOf(),并判断该方法返回值.若返回值val为原始类型值,则[[DefaultValue]] (String)返回值为val.
  3. 如果两次调用的方法的返回值均不是原始类型,则抛出类型错误(TypeError).

当hint为’Number’时:
将上述两个方法的调用顺序颠倒(先调用valueOf(),再调用toString())

当hint值为省略时:
除时间对象(Date object)的hint为’String’外,其余对象的hint值默认为’Number’.

√要点二:js数据类型转换的四个主要抽象操作(abstract operation)

  1. 当需要时,ECMAScript的运行时系统会自动进行数据类型的转换.为了阐明这一过程中的语法结构,我们定义了一系列的抽象操作来作为工具帮助说明(就像用地球仪说明地球的形状结构). 这些抽象操作并不是语言本身的组成部分,我们只是用它们来帮助说明语言的语法结构.
    2.数据转换的四个主要抽象操作:
    ToPrimitive ( input [ , PreferredType ] )----将对象类型转换为原始类型
    ToBoolean ( argument )----其他类型转为布尔类型
    ToNumber ( argument )----其他类型转为数值类型
    ToString ( argument )----其他类型转为字符串类型

数据类型转换的四个主要抽象操作(abstract operation)

ToPrimitive ( input [ , PreferredType ] )
1.参数
input: 被转换的数据
preferredType: 期望的类型,同上文的hint
2.实现逻辑:

  • 如果input本身就是原始类型,返回input本身.
  • 如果input是对象类型,那么用input调用其[[DefaultValue]] (preferredType),得到返回值PrimValue.
    总结:抽象操作ToPrimitive是依赖对象的内部方法[[DefaultValue]] (hint)实现的

ToNumber ( argument )

总结:抽象操作ToNumber,先将对象由ToPrimitive转换成原始类型,再按原始类型判断结果值

ToString ( argument )

总结:抽象操作ToString,先将对象由ToPrimitive转换成原始类型,再按原始类型判断结果值


类型转换与对象加法小结:
1.js数据类型转换:

  • 显式的强制转换(用Number(),parseInt()等)
  • 隐式的转换,如:
    • +,==,比较运算符对数据的隐式转换
    • 表达式对某处的值的期待类型(例如:if语句中的分支判断条件)

2.无论是强制还是隐式转换,都遵循四个主要的抽象操作.
3.对象类型的转换是基于内部方法[[DefaultValue]] (hint)实现,而该内部方法又是依靠其他两个方法(非内部方法)valueOf(),toString()实现.
4.如果我们修改valueOf()与toString(),我们就可以自己控制对象的类型转换结果.举例:

  • 情况1:报错–TypeError

    **说明:**自定义valueOf()与toString()的返回值都是空对象(非原始类型值), js报错TypeError,符合预期结果.
  • 情况2:一组比较

    交换两个方法的返回值:

    结论1: "+"号导致的一般对象隐式转为原始类型时,[[DefaultValue]]步骤确实是先调valueOf().

    交换两个方法的返回值:

    结论2: 时间对象Date被隐式转换时,[[DefaultValue]]步骤确实是先调toString().

余数运算符–%

  1. 余数运算符%–返回前一个数被后一个数除,所得的余数.
  2. 余数的正负号由被除数(位于%之前的数)决定,因此,为了得到正确的负数的余数,需要先用绝对值函数Math.abs(x)处理.
  3. 余数运算符可以用于浮点数,但是运算结果不准确.

自增++与自减–运算符

  1. 功能:将运算数先转变为数值类型,然后加上1(++)或者减去1(–).

  2. ++与–都是一元运算符,只需要一个操作数.

  3. 副作用----js中仅有的两个会改变变量本身值的运算符:

    • js中,其他运算符本身并不会改变其存储空间里的值.(原始类型按值传递,操作的实际上是值的拷贝;复合类型按引用传递,修改的是对象类型的转为原始类型的值的拷贝,本身不变).
      举例:

      **说明:**上图中,test1 = test对象转为数值类型的拷贝 - 1,而test对象本身不变.
    • 但是,自增和自减运算符会改变值本身,也就是改变存储空间中的值.它们操作的是值本身,而不是任何形式的拷贝.
      举例:

      **说明:**上图中,test本身从对象变成了2.
  4. 书写位置与作用时机的问题:

    • –/++,写在运算数之前,先自增/自减,再以新的值参与表达式运算
    • –/++,写在运算数之后,先以旧值参与表达式运算,再自增/自减.

数值运算符,负数值运算符

  1. 数值运算符("+"):"+"的作用很广泛,两数之间是加号,两运算子一方为字符串又成了字符串连接符,如果只作用于一个运算子便是数值运算符.作用是将运算子转换为数值.
  2. 负数值运算符:一元运算符,先将运算子转换为数值,再取它的负值.
  3. 多个数值/负数值运算符同时使用时,注意要与自增和自减运算符区分开来,不然容易语义上出错.

指数运算符

  1. 指数运算符:即"**"(两个乘号),二元运算符.位于该运算符之前的运算子为底数,之后的为幂.
  2. 结合性:指数运算符是右结合的,这与我们数学上的概念是相吻合的.例如: