Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

类型转换 #15

Open
wweggplant opened this issue Sep 25, 2023 · 0 comments
Open

类型转换 #15

wweggplant opened this issue Sep 25, 2023 · 0 comments

Comments

@wweggplant
Copy link
Owner

javascript中的类型转换。可以说是一种灾难。情况复杂繁多,而且很多时候得到的结果会显得莫名其妙。在讲述类型转换之前,先来看下比较简单的javascript中有六种基础类型的转换:UndefinedNullBooleanNumberStringObject

在总结规范和一些博文后,我发现有两个类型和一个方法比较重要。两个类型是StringNumber,一个方法是toPrimitive

原始值转原始值

基础类型之间的转换比较简单。

转boolean

在javaScript 中,使用Boolean函数进行转换的话,只有 6 种值可以被转换成 false,其他都会被转换成 true。注意不传参数的情况下,默认参数为false

console.log(Boolean()) // 默认情况是false

// 以下六种会返回false
console.log(Boolean(undefined)) // false
console.log(Boolean(null)) // false
console.log(Boolean(+0)) // false
console.log(Boolean(-0)) // false
console.log(Boolean(NaN)) // false
console.log(Boolean("")) // false

转数字

转数字使用Number函数,内部会调用ToNumber,转换的结果如下:

参数类型 结果
Undefined NaN
Null +0
Boolean true对应1, false对应0
Number 没有发生转换
String 看下面的详细解释
Object 调用toPrimitive方法转换, 再把把结果传入ToNumber返回结果, 后面有介绍

String转换成Number总体来说,如果字符串不是字面的数字(StringNumericLiteral, 我的理解是字符表示数字),那么结果就是NaN。在解析字符的时候,会忽略所有的前导0和空格(和换行),如果字符是前缀是0x或者0X,则会使用16进制进行解析。

要注意,这个`toNumber`要比`parseInt`和`parseFloat`方法更加严格。字符串`100af`用`parseInt`是可以转换成`100`的,但是调用`toNumber`的话,结果是`NaN`

转换成String

Number类似,字符串转换,内部会调用ToString方法,这个方法是内部方法,不同于大家知道的这Object原型里的方法toStringToString转换结果如下表

参数类型 结果
Undefined "undefined"
Null "null"
Boolean true转换成"true", false对应"false"
Number 没有发生转换
String 返回传入的字符串
Object 与ToNumber类似,调用toPrimitive方法转换, 再把把结果传入ToString返回结果, 后面有介绍

Number转换成String

String(m)

具体规范这个参考这里 9.8

  1. 如果m是NaN,则返回NaN
  2. 如果m是+0-0,则返回0
  3. 如果小于0
    ....

常见的对象转字符串

  1. 数组的 toString 方法将每个数组元素转换成一个字符串,并在元素之间添加逗号后合并成结果字符串。
  2. 函数的 toString 方法返回源代码字符串。
  3. 日期的 toString 方法返回一个可读的日期和时间字符串。
  4. RegExp 的 toString 方法返回一个表示正则表达式直接量的字符串。

toPrimitive

这个方法主要是用在对象转原始值的情况下。在上面的小节中表格有提到对象转字符和数字的情况。大体的思路其实就是先调用toPrimitive方法,然后把得到的结果传入ToString/ToNumber中,得到最后的结果。toPrimitive方法有两个参数,一个是需要转换的值,另一个就是可选参数的转换类型(有两种String和Number)。根据规范toPrimitive的规则如下:

  1. 如果传入的是基础类型的值,则不会发生转换
  2. 如果指定了要转换的类型,则:
    • 如果要转换成字符:
      1. 如果可以调用toString方法,则调用方法, 如果得到的是一个原始类型的值就返回结果
      2. 如果可以调用valueOf方法, 如果得到的是一个原始类型的值就返回结果
      3. 抛出一个TypeError异常
    • 如果要转换成数字:
      1. 如果可以调用valueOf方法, 如果得到的是一个原始类型的值就返回结果
      2. 如果可以调用toString方法, 如果得到的是一个原始类型的值就返回结果
      3. 抛出一个TypeError异常
  3. 如果没有指定要转换类型,除非传入的是日期类型会转换成String,否则默认转换成Number。

隐形转换的情况

+运算符号、==if条件语句和逻辑运算(&& ||)的情况下,会发生类型转换

+号们

一个+的时候,和Number是一样的

二元操作符 value1 + value2

一个和基础的问题就是,1+'1'是数字转换成字符串,那么1 + {}呢?

执行value1 + value2的过程是这样,根据规范总结如下:

  1. 得到左操作数的引用lref, 通过GetValue得到值lval
  2. 同理得到右操作数的值rval
  3. 通过ToPrimitive分别计算lvalrval,得到个子的原始值lprimrprim
  4. 类型判断,如果lprim或者rprim是字符类型,那么就使用字符串拼接方法,否则转换成数字类型

回答上面提到的问题,1 + {}分别计算左右的原始值。1不用转换,{}转换成原始值,根据上面的toPrimitive小节我们知道,先调用valueOf转换成{},不是原始值,再调用toString(),结果是[object Object]。规范中还涉及到最后计算的结果,这里就不讨论了,可以参考具体的规范

相等==

宽松相等会有转换的情况发生,有兴趣的可以看下规范,我觉得没有必要去记忆,最好的办法就是使用严格相等(===),避免会发生到的怪癖情况,编程是工作,而不是考八股文。

  1. 规范
  2. 中文简析

|| 与 &&

|| &&并不能算是逻辑运算符,应该叫选择器运算符.|| &&返回结果不一定是一个布尔值,二是两个操作数里面的一个.||&&首先会对第一个操作数(a 和 c)执行条件判断,如果其不是布尔值(如上例)就先进行 ToBoolean强制类型转换,然后再执行条件判断。对于||来说,如果条件判断结果为true就返回第一个操作数(a 和 c)的值,如果为false就返回第二个操作数(b)的值。&&则相反,如果条件判断结果为true就返回第二个操作数(b)的值,如果为false就返回第一个操作数(a 和 c)的值。

||使用

function foo(a,b) {
    a = a || "hello"; 
    b = b || "world";
    console.log( a + " " + b ); 
}
foo(); // "hello world"
foo( "yeah", "yeah!" ); // "yeah yeah!"

&&使用,可以看到,b最后是0,并不是布尔值

function foo() {
    console.log( a );
}
var a = 0;
var b = a && foo(); // b = 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant