Skip to content

JavaScript深入系列之强制类型转换 #6

Description

@archerU

内置类型

JavaScript 有七种内置类型。

  • 空值(null)
  • 未定义(undefined)
  • 字符串(string)
  • 数字(number)
  • 布尔值(boolean)
  • 对象(object)
  • 符号(symbol,ES6 中新增)

检测

typeof undefined === "undefined"; // true
typeof "42" === "string"; // true
typeof 42 === "number"; // true
typeof true === "boolean"; // true
typeof { life: 42 } === "object"; // true

比较特殊的存在

typeof null === "object"; // true
typeof function a(){/*..*/} === "function"; // true

function(函数)也是 JavaScript 的一个内置类型。它实际上是 Object 的一个“子类型”。

常用原生函数

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Error()
  • Symbol() --- ES6 中新加入的!
var a = new String("abc");

typeof a; // 是 "object",不是 "String"

a instanceof String; // true

Object.prototype.toString.call( a ); // "[object String]"

typeof 只能用于判断基本数据类型,无法判断复杂数据类型。对于所有 typeof 返回值为 "object" 的对象(如数组)都包含一个内部属性 [[Class]] (我们可以把它看作一个内部的分类,而非传统的面向对象意义上的类)。这个属性无法直接访问,一般通过 Object.prototype.toString(..) 来查看。

Object.prototype.toString.call([1,2,3]);
// "[object Array]"

Object.prototype.toString.call(/regex-literal/i);
// "[object RegExp]"

Null() 和 Undefined() 这样的原生构造函数并不存在,但是内部 [[Class]] 属性值仍然是 "Null" 和 "Undefined"。其它基本类型被各自的封装对象自动包装起来,所以它们的内部 [[Class]] 属性值分别为 "String","Number","Boolean"。

Object.prototype.toString.call("abc");
// "[object String]"

Object.prototype.toString.call(null);
// "[object Null]"

Object.prototype.toString.call(1);
// "[object Number]"

Object.prototype.toString.call(true);
// "[object Boolean]"

Object.prototype.toString.call(undefined);
// "[object Undefined]"

Object.prototype.toString.call(Symbol(123))
// "[object Symbol]"

这里的 Symbol 非常特殊,我们可以使用 Symbol(..) 原生构造函数来自定义符号。但不能带 new 关键字,否则会出错。

强制类型转换

ToString

toString() 负责处理非字符串到字符串的强制类型转换。

基本类型的字符串化规则:null 转换为 "null"undefined 转换为 "undefined"true 转换为 "true"。数字的字符串化遵守通用规则,极小和极大的数字使用指数形式。

// 1.07 连续乘以七个 1000
var a = 1.07 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000;

// 七个 1000 一共 21 位数字
a.toString(); // "1.07e21"

普通对象的字符串化规则:除非对象自定义了 toString() 方法,否则 toString() (Object.prototype.toString()) 返回内部属性 [[Class]] 的值。

// 普通对象
var a = {b: "123"};

a.toString(); // "[object Object]"
// 数组
var a = [1,2,3];

a.toString(); // "1,2,3"

数组的默认 toString() 方法经过了重新定义,将所有单元字符串化后再用 "," 连接起来。

ToNumber

基本类型的数字化规则:true 转换为 1,false 转换为 0。undefined 转换为 NaNnull 转换为 0。字符串的处理基本遵循数字常量的相关规则/语法。处理失败时返回 NaN(处理数字常量失败时会产生语法错误)。

对象(包括数组)会首先被转换为相应的基本类型值。首先检查是否有 valueOf() 方法,如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString() 的返回值(如果存在)来进行强制类型转换。如果 valueOf()toString() 都不返回基本类型值,会产生 TypeError 错误。

Object.create(null) 创建的对象 [[Prototype]] 属性为 null,并且没有 valueOf()toString() 方法,因此无法进行强制类型转换。

var a = Object.create(null);

Number(a);

// VM406:1 Uncaught TypeError: Cannot convert  
// object to primitive value
// at Number (<anonymous>)
//  at <anonymous>:1:1

ToBoolean

假值:

  • undefined
  • null
  • false
  • +0、-0 和 NaN
  • ""

除了假值以外的值都是真值。

显示强制类型转换

字符串和数字之间的显示转换

String(..) 和 Number(..)

String(..) 遵循 ToString 规则,Number(..) 遵循 ToNumber 规则。

var a = 42;
var b = String(a);

var c = "3.14";
var d = Number(c);

b; // "42"
d; // 3.14

toString() 、 一元运算符 +~ 运算符(即字位操作 “非“)

var a = 42;
var b = a.toString();

var c = "3.14";
var d = +c;

b; // "42"
d; // 3.14

显示转换为布尔值

Boolean(..) 遵循 ToBoolean 规则。

var a = "0";
var b = [];
var c = {};

var d = "";
var e = 0;
var f = null;
var g;

Boolean(a); // true
Boolean(b); // true
Boolean(c); // true

Boolean(d); // false
Boolean(e); // false
Boolean(f); // false
Boolean(g); // false

一元运算符 !!!

一元运算符 ! 显式地将值强制类型转换为布尔值。但是它同时还将真值反转为假值(或者将假值反转为真值)。所以显式强制类型转换为布尔值最常用的方法是!!

var a = "0";
var b = [];
var c = {};

var d = "";
var e = 0;
var f = null;
var g;

!!a; // true
!!b; // true
!!c; // true

!!d; // false
!!e; // false
!!f; // false
!!g; // false

隐式强制类型转换

字符串和数字之间的隐式转换

+ 运算符即能用于数字加法,也能用于字符串拼接。如果 + 的其中一个操作数是字符串则执行字符串拼接,否则执行数字加法。

var a = "42";
var b = "0";

var c = 42;
var d = 0;

a + b; // "420"
c + d; // 42
a + d; // "420"

-*/ 都只适用于数字,因此会强制类型转换为数字。

var a = "3.14";
var b = a - 0;

b; // 3.14

隐式转换为布尔值

  1. if (..) 语句中的条件判断表达式
  2. for ( .. ; .. ; .. ) 语句中的条件判断表达式(第二个)
  3. while (..) 和 do..while(..) 循环中的条件判断表达式
  4. ? : 中的条件判断表达式
  5. 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)

以上遵循 ToBoolean 规则。

宽松相等和严格相等

==允许在相等比较中进行强制类型转换,而===不允许。

11.9.3 节中规定, == 在比较两个不同类型的值时会发生隐式强制类型转换,会将其中之一或两者都转换为相同的类型后再进行比较。
11.9.3.1 的最后定义了对象(包括函数和数组)的宽松相等 == 。两个对象指向同一个值时即视为相等,不发生强制类型转换。

字符串和数字之间的相等比较

字符串强制转换为数字比较

如果 Type(x) 是数字,Type(y) 是字符串,则返回 x == ToNumber(y) 的结果。
如果 Type(x) 是字符串,Type(y) 是数字,则返回 ToNumber(x) == y 的结果。

其它类型和布尔值之间的比较相等

布尔值强制类型转换为数字比较

如果 Type(x) 是布尔类型,则返回 ToNumber(x) == y 的结果。
如果 Type(y) 是布尔类型,则返回 x == ToNumber(y) 的结果。

null和undefined之间的相等比较

null 和 undefined 相等(它们也与自身相等),除此之外不与其它任何值相等

对象和非对象之间的相等

对象强制类型转换为基本数据类型值

如果 Type(x) 是字符串或数字,Type(y) 是对象,则返回 x == ToPrimitive(y) 的结果。
如果 Type(x) 是对象,Type(y) 是字符串或数字,则返回 ToPrimitive(x) == y 的结果。

抽象关系比较

“抽象关系比较”,分为两个部分:比较双方都是字符串和其他情况。
比较双方首先调用 ToPrimitive ,如果结果出现非字符串,就根据 ToNumber 规则将双方强制类型转换为数字来进行比较。
比较双方都是字符串,则按字母顺序来进行比较。

安全运用隐式强制类型转换

  • 如果两边的值中有 true 或者 false,千万不要使用 ==
  • 如果两边的值中有 []"" 或者 0,尽量不要使用 ==

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions