◆

JavaScript 类型转换:显式与隐式转换规则详解

2023-02-02 01:370

显式转换

  • 通过手动进行类型转换,Javascript 提供了以下转型函数:
    • 转换为数值类型:Number(mix)、parseInt(string,radix)、parseFloat(string)
    • 转换为字符串类型:toString(radix)、String(mix)
    • 转换为布尔类型:Boolean(mix)

String(mix)

将任何类型的值转换为字符串,其规则为:

  • 如果有 toString() 方法,则调用该方法(不传递 radix 参数)并返回结果
  • 如果是 null,返回 "null"
  • 如果是 undefined,返回 "undefined"
  • String 方法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式。
    • 先调用对象自身的 toString 方法。如果返回原始类型的值,则对该值使用 String 函数,不再进行以下步骤。
    • 如果 toString 方法返回的是对象,再调用原对象的 valueOf 方法。如果 valueOf 方法返回原始类型的值,则对该值使用 String 函数,不再进行以下步骤。
    • 如果 valueOf 方法返回的是对象

boolean

  • 是一个布尔值的对象包装器。
  • 第一个参数传递的值将转换为布尔值。如果省略或值0,-0,0n, -0n,null,false,NaN,undefined,或空字符串("" || `` || ''),该对象具有的初始值 false。
  • 所有其他值,包括任何对象,空数组([])或字符串"false",都会创建一个初始值为 true 的对象。
  • 不要将基本类型中的布尔值 true 和 false 与值为 true 和 false 的 Boolean 对象弄混了。
  • 其值不是 undefined 或 null 的任何对象(包括其值为 false 的布尔对象)在传递给条件语句时都将计算为 true。 例如,以下 if 语句中的条件评估为 true:
var x = new Boolean(false); // 一个值为 false 的 Boolean 对象
// object
console.log(typeof x);
if (x) {
  console.log('这里的代码会被执行');
}

// ===

var x = Boolean(false);
// boolean
console.log(typeof x);
if (x) {
  console.log('这里的代码不会被执行');
}

Number

  • Number(mix) 函数,可以将任意类型的参数 mix 转换为数值类型。其规则为:
    • (1) 如果是布尔值,true 和 false 分别被转换为 1 和 0
    • (2) 如果是数字值,返回本身。
    • (3) 如果是 null,返回 0.
    • (4) 如果是 undefined,返回 NaN。
    • (5) 如果是字符串,遵循以下规则:
      • 1、如果字符串中只包含数字,则将其转换为十进制(忽略前导 0)
      • 2、如果字符串中包含有效的浮点格式,将其转换为浮点数值(忽略前导 0)
      • 3、如果是空字符串,将其转换为 0
      • 4、如果字符串中包含非以上格式,则将其转换为 NaN
    • (6) 如果是对象
      • 调用对象自身的 valueOf 方法。如果返回原始类型的值,则直接对该值使用 Number 函数,不再进行后续步骤。
      • 第二步,如果 valueOf 方法返回的还是对象,则改为调用对象自身的 toString 方法。如果 toString 方法返回原始类型的值,则对该值使用 Number 函数,不再进行后续步骤。
      • 第三步,如果 toString 方法返回的是对象,就报错。
console.log(Number(false)); // 0
console.log(Number(-0)); // -0
console.log(Number(Infinity)); // Infinity
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
console.log(Number(0x11)); // 17
console.log(Number('03   ')); // 3
console.log(Number('   03x')); // NaN
console.log(Number('0x03')); // 3
console.log(Number(' +0 ')); // 0
console.log(Number('0   5')); // NaN
console.log(Number('0a3')); // NaN
console.log(Number('  0.3')); // 0.3
console.log(Number('0.3.2 ')); // NaN
console.log(Number('null')); // NaN
console.log(Number('')); // 0
console.log(Number(new Number(3))); // 3
console.log(Number(new String('null'))); // NaN
console.log(Number({ a: 3 })); // NaN
console.log(Number({})); // NaN
console.log(Number([3, 4])); // NaN
console.log(Number([])); // 0
console.log(Number([1])); // 1
console.log(Number(['333'])); // 333

隐式转换

逻辑运算符

  • 逻辑与,AND(&&) expr1 && expr2 若 expr1 可转换为 true,则返回 expr2;否则,返回 expr1。
  • 逻辑或,OR(||) expr1 || expr2 若 expr1 可转换为 true,则返回 expr1;否则,返回 expr2。
  • 逻辑非,NOT(!) !expr 若 expr 可转换为 true,则返回 false;否则,返回 true。
false && true; // 返回 false
'Cat' && 'Dog'; // 返回 "Dog"
false && 'Cat'; // 返回 false
'Cat' && false; // 返回 false
'' && false; // 返回 ""
false && ''; // 返回 false
NaN && ''; // 返回 NaN

'' || false; // 返回 false
false || ''; // 返回 ""

关系操作符(<, >, <=, >=)

关系操作符的操作值也可以是任意类型的,所以使用非数值类型参与比较时也需要系统进行隐式类型转换:

  • 如果两个操作值都是数值,则进行数值比较
  • 如果两个操作值都是字符串,则比较字符串对应的字符编码值
  • 如果只有一个操作值是数值,则将另一个操作值转换为数值,进行数值比较
  • 如果一个操作数是对象,则调用 valueOf() 方法(如果对象没有 valueOf() 方法则调用 toString() 方法),得到的结果按照前面的规则执行比较
  • 如果一个操作值是布尔值,则将其转换为数值,再进行比较 注:NaN 是非常特殊的值,它不和任何类型的值相等,包括它自己,同时它与任何类型的值比较大小时都返回 false。

一元加/减值符(+/-)

一元运算符, 如果操作数在之前不是 number,试图将其转换为 number

console.log(+[]); // 0
console.log(+[1, 2]); // NaN
console.log(+true); // 1

加法运算操作符

  • 如果两个操作值都是数值,其规则为:
  • 如果一个操作数为 NaN,则结果为 NaN
  • 如果是 Infinity+Infinity,结果是 Infinity
  • 如果是 -Infinity+(-Infinity),结果是 -Infinity
  • 如果是 Infinity+(-Infinity),结果是 NaN
  • 如果是 +0+(+0),结果为 +0
  • 如果是 (-0)+(-0),结果为 -0
  • 如果是 (+0)+(-0),结果为 +0
  • 如果两个操作值都是字符串,则将它们拼接起来
  • 如果只有一个操作值为字符串,则将另外操作值转换为字符串,然后拼接起来
  • 如果一个操作数是对象、数值或者布尔值,则调用 toString() 方法取得字符串值,然后再应用前面的字符串规则。对于 undefined 和 null,分别调用 String() 显式转换为字符串。
  • 可以看出,加法运算中,如果有一个操作值为字符串类型,则将另一个操作值转换为字符串,最后连接起来。
console.log(1 + 'str'); // '1str'
console.log(1 + 3); // 4
console.log(1 + '3'); // '13'
console.log('1' + '3'); // '13'
console.log(1 + []); // '1'
console.log(1 + {}); // '1[object Object]'
console.log(1 + function test() {}); // '1function test(){}'
console.log([] + {}); // '[object Object]'
console.log([1] + {}); // '1[object Object]'
console.log([1] + [2]); // '12'
console.log(['3'] + {}); // '3[object Object]'
console.log(['4', '5'] + {}); // '4,5[object Object]'
console.log(['4', '5'] + 1); // '4,51'
console.log(['4', '5'] + '1'); // '4,51'

乘除、减号运算符、取余运算符

这些操作符针对的是运算,所以他们具有共同性:如果操作值之一不是数值,则被隐式调用 Number() 函数进行转换。

相等操作符 == or !=

  • ==,转化成同一类型后的值,看“值”是否相等。
  • ===,如果类型不同,其结果就是不等。
  • 相等操作符( == or !=)会对操作值进行隐式转换后进行比较:
  • 布尔类型与其它任何类型进行比较,布尔类型将会转换为 number 类型,如果为 true,那么会转换为 1,如果为 false,会转换为整数 0,即 0。
  • 如果一个操作值为字符串,另一个操作值为数值,则通过 Number() 函数将字符串转换为数值
  • 如果一个对象与数字或字符串相比较,JavaScript 会尝试返回对象的默认值。操作符会尝试通过方法 valueOf 和 toString 将对象转换为其原始值(一个字符串或数字类型的值)。如果尝试转换失败,会产生一个运行时错误。
  • null 与 undefined 是相等的
  • 如果一个操作值为 NaN,则相等比较返回 false
  • 如果两个操作值都是对象,则比较它们是不是指向同一个对象
  • number 类型或 string 类型与 object 类型进行比较,number 或者 string 类型都会转换为 object 类型
// true
console.log(null == undefined);
// false
console.log(NaN == NaN);
// false
console.log(null === undefined);
// true
console.log(0 === -0);
// false
console.log({ a: 1 } == { a: 1 });
// true
console.log({ a: 1 } == '[object Object]');

other

空值合并运算符

当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。

const foo = null ?? 'default string';
// default string
console.log(foo);

const baz = 0 ?? 42;
// 0
console.log(baz);
  • 以前,如果想为一个变量赋默认值,通常的做法是使用逻辑或操作符(||):
  • 然而,由于 || 是一个布尔逻辑运算符,左侧的操作数会被强制转换成布尔值用于求值。任何假值 (0, '', NaN, null, undefined)都不会被返回。这导致如果你使用0,''或NaN作为有效值,就会出现不可预料的后果。
let foo;
let someDummyText = foo || 'Hello!';

let count = 0;
let text = '';

let qty = count || 42;
let message = text || 'hi!';
console.log(qty); // 42,而不是 0
console.log(message); // "hi!",而不是 ""

let myText = '';

let notFalsyText = myText || 'Hello world';
console.log(notFalsyText); // Hello world

let preservingFalsy = myText ?? 'Hi neighborhood';
console.log(preservingFalsy); // ''
  • 不能与 AND 或 OR 操作符共用
null || undefined ?? "foo"; // 抛出 SyntaxError
true || undefined ?? "foo"; // 抛出 SyntaxError

// 用括号包裹
(null || undefined) ?? "foo"; // 返回 "foo"

可选链操作符

  • 可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。
  • ?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为空(null 或者 undefined) 的情况下不会引起错误,该表达式短路返回值是 undefined。
  • 与函数调用一起使用时,如果给定的函数不存在,则返回 undefined。
  • 当使用方括号与属性名的形式来访问属性时,你也可以使用可选链操作符:
let nestedProp = obj?.['prop' + 'Name'];
  • 可选链不能用于赋值
let object = {};
object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment
  • 可选链访问数组元素
let arrayItem = arr?.[42];
  • 使用空值合并操作符
let customer = {
  name: 'Carl',
  details: { age: 82 },
};
let customerCity = customer?.city ?? '暗之城';
console.log(customerCity); // “暗之城”

Object.js

  • 不和 == 一样,它不会进行隐式类型转换
  • 与 === 的区别如下
Object.is(0, -0); // false
Object.is(+0, -0); // false
Object.is(0, +0); // true
Object.is(-0, -0); // true
Object.is(NaN, 0 / 0); // true

模板字符串

  • 如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的 toString 方法。
var obj = {};
// [object Object]
console.log(`${obj}`);
// true
console.log(obj.toString() === `${obj}`);

对象转原始类型

对象转原始类型,会调用内置的 [ToPrimitive] 函数,对于该函数而言,其逻辑如下:

  • 如果有 Symbol.toPrimitive() 方法,优先调用再返回
  • 调用 valueOf(),如果转换为原始类型,则返回
  • 调用 toString(),如果转换为原始类型,则返回
  • 如果都没有返回原始类型,会报错
var obj = {
  value: 3,
  valueOf() {
    return 4;
  },
  toString() {
    return '5';
  },
  [Symbol.toPrimitive]() {
    return 6;
  },
};
console.log(obj + 1); // 输出7

// ===

var obj = {
  value: 3,
  valueOf() {
    return 4;
  },
  toString() {
    return '5';
  },
};
console.log(obj + 1); // 输出5

// ===

var obj = {
  value: 3,
  toString() {
    return '5';
  },
};

console.log(obj + 1); // 输出51 注意 toString 返回的是一个字符串5
// ===

var obj = {
  value: 3,
};
// obj.toString == [object Object] ==> [object Object]1
console.log(obj + 1);

Symbol.toPrimitive

  • 一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。
  • 在 Symbol.toPrimitive 属性(用作函数值)的帮助下,一个对象可被转换为原始值。
  • 该函数被调用时,会被传递一个字符串参数 hint ,表示要转换到的原始值的预期类型。
  • hint 参数的取值是 number、string 和 default 中的任意一个。
// 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果
var obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ''); // "[object Object]"

// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果
var obj2 = {
  [Symbol.toPrimitive](hint) {
    if (hint == 'number') {
      return 10;
    }
    if (hint == 'string') {
      return 'hello';
    }
    return true;
  },
};
console.log(+obj2); // 10      -- hint 参数值是 "number"
console.log(`${obj2}`); // "hello" -- hint 参数值是 "string"
console.log(obj2 + ''); // "true"  -- hint 参数值是 "default"

如何让 if(a == 1 && a == 2) 条件成立?

var a = {
  value: 0,
  valueOf: function () {
    this.value++;
    return this.value;
  },
};
// true
console.log(a == 1 && a == 2);

[] == ![]结果是什么?为什么?

  1. 首先可以看成是 对象([])和布尔值(![])的比较
  2. == 中,布尔值先转换成数字 ![] 转换为 0
  3. 变成对象([])和数值(0)的比较
  4. 应用对象的 toString 方法将 [] 转换为 ""
  5. 变成字符串("")和数值(0)的比较
  6. 将字符串("")转换为数值 0
  7. 0 == 0 结果为 true
💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEJavaScript 类型转换:显式与隐式转换规则详解加载中