文章讲隐式强制类型转换的路线是从基础讲到应用, 这条学习曲线比较平滑. 具体的路线为:正则表达式
基本数据类型引出包装类型的概念 ==> valueOf 和 toString 方法 ==> ToPrimitive 抽象操做和 [[DefaultValue]] 操做 ==> 各类类型之间类型转换的规律 ==> 常见引发隐式类型转换的状况 ==> 多个实例练习以检验方法的有用性.数组
讨论基本包装类型的前提是了解基本数据类型(也能够称为原始类型, 简单数据类型等)。而后经过基本数据类型调用方法的行为, 引出基本包装类型的概念和做用.函数
已经知道 JavaScript 中共有6种基本数据类型,分别是:string,number,boolean,null,undefined,symbol.学习
基本类型既不是对象, 也没有方法可供调用. 然而常常见到基本类型的变量调用方法的状况, 类如:ui
let index = 'ABCDE'.indexOf('CD');
console.log(index); // 2
复制代码
上例中 'ABCDE'
是一个字符串的基本类型, 这个字符串直接调用了indexOf
方法, 将该方法的返回值保存在变量 index
中, 而后在第二行输出. 能够看到这个例子可以运行而且结果正确.spa
既然上面说原始类型没有方法可供调用, 那么字符串 'ABCDE'
在调用 indexOf
方法时为何没有出错呢? 并且从运行结果来看, indexOf
这个方法确实被调用了, 那么是谁调用了这个方法而且返回了正确的结果呢? 答案就是基本包装类型. 下面引入基本包装类型的相关内容.prototype
关键词: 包装, 基本包装类型, 基本包装类型的对象code
上一节讨论到其实调用方法的并非字符串自己 , 而是基本包装类型. 下面就应该具体讨论基本包装类型的相关内容了.对象
为了基本类型能够正常调用方法, 后台会为这个基本类型的值自动建立一个对应的的对象, 这个对象的类型就称为基本包装类型, 而后用这个对象去调用方法, 在调用完方法以后, 这个对象就会被销毁( 用完就销毁 )了. 这个从基本数据类型生成基本包装类型对象的过程称为包装( box ).继承
简单来讲, 在基本类型调用方法的时候, 方法的真正调用者其实不是咱们直接定义的基本类型, 而是后台给咱们建立的基本包装类型的 对象 . 并且这个对象是临时的、不会一直存在的、用完就会被销毁的.
这个对象还有一个特色, 他是与基本类型的值相对应的. 然而:
并非每一种基本类型都有对应的包装类型
上节中提到的六种基本类型中的四种有其对应的包装类型, 分别是:
string(字符串) -> String, number(数值) -> Number,boolean(布尔) -> Boolean,symbol(符号) -> Symbol
其中符号 ->
表示对应
. 同时: 注意首字母的大写( 对象名的首字母习俗默认大写 ).
注意: 本文只讨论 string,number,boolean
三种基本类型及其对应的包装类型.
还须要了解的是, 不只仅是系统能够隐式的建立包装对象, 用户也能够手动、显式的建立一个基本包装类型的对象, 方法为: 用关键字 new
加上对应的包装类型的构造函数, 参数传入基本类型的值便可, 例如:
let n = new Number(22); // n 是 数值22对应的包装对象
let str = new String('example'); // str 是字符串 'example' 对应的包装对象
let flag = new Boolean(false); // flag 是布尔值 false 对应的包装对象
复制代码
至此, 再看第一节的这个例子, 能够再次想象系统建立包装对象的大体过程:
let index = 'ABCDE'.indexOf('CD');
console.log(index); // 2
复制代码
能够发现第一行代码中后台发生了包装行为: 后台根据字符串类型的 'ABCDE'
包装出了一个对象. 即: 在调用 indexOf
方法时, 后台发现想调用这个方法的是一个基本类型的值, 可是这个值没有这个方法可供调用, 因而为它生成了对应的包装对象( 操做 1 ), 而后经过这个包装对象来调用了 indexOf
方法( 操做 2 ), 然后将方法的返回值赋给了变量 index
. 最后, 把这个包装对象销毁( 操做 3 ).
根据以上思路,能够大体模拟出上述过程的对应的代码:
// step 1. 建立 'ABCDE' 对应的基本包装类型的对象:
let temp = new String('ABCDE');
// step 2. 用包装类型的对象 temp 调用 indexOf 方法, 并将返回值赋给 index 变量:
let index = temp.indexOf('CD');
// step 3. 将 temp 对象销毁
temp = null;
复制代码
当一个基本数据类型想要调用方法时, 后台会为它生成一个临时的包装对象, 利用这个对象去调用方法, 再将方法执行的结果返回, 随后这个临时对象被销毁.
从上面的内容了解到 包装(box) 是根据一个基本类型的值生成一个对应类型的对象的过程, 与这个过程大体相反, 存在一种根据包装对象生成基本类型值的过程, 可称为 拆包装 (unbox). 这个过程一样便可以由后台隐式的完成, 也能够手动的调用方法 valueOf
来作.
下一节开始讨论 valueOf
这个方法, 同时引出另一个一样重要的方法 toString
.
valueOf
和 toString
valueOf
方法基本包装类型的拆包装操做用到了包装对象中的 valueOf
函数, 这个函数能够将一个对象转换成一个基本类型的值.
对于 Boolean, Number 和 String
三者的基本包装对象来讲, 调用 valueOf
的返回值是各自对应的基本数据类型的值:
let n = new Number(22); // 包装基本数值数据 22
// 拆包装出来的结果是对应的基本数据类型的值
console.log(n.valueOf() === 22); // true
let str = new String('example'); // 包装基本字符串数据 'example'
// 拆包装出来的结果是对应的基本数据类型的值
console.log(str.valueOf() === 'example'); // true,
let flag = new Boolean(false); // 包装基本布尔数据 false
// 拆包装出来的结果是对应的基本数据类型的值
console.log(flag.valueOf() === false); // true
复制代码
有时会发生后台隐式拆包装的状况, 包装类型的对象会在后台调用 valueOf
方法, 例如:
let a = new Number(1);
let b = a + 1; //---> 这一行发生了隐式拆包装操做: let b = a.valueOf() + 1;
console.log(b); // b 为 2
console.log(typeof a); // object
console.log(typeof b); // number, b 的类型为 基本数据类型, 而不是包装类型
复制代码
甚至一行代码中会发生包装和解包装两种操做, 例如:
let num = 3.14159;
console.log(num.valueOf()); // 3.14159
复制代码
在上面代码块的第二行代码中变量 num
要调用函数 valueOf
, 此时 num
会被先包装为 基本包装类型的对象,而这个对象在调用 valueOf
方法时就发生了解包装的操做.
valueOf
方法不只仅是基本包装类型有 valueOf
方法, 许多 JavaScript 内建(build-in)对象都有该函数, 为使执行的结果与对象自己相符合, 大多对象都重写了这个方法. 下面看一些其余对象的 valueOf
方法的行为有什么特色.
如下列出经常使用内置对象的 valueOf
方法的返回值:
对象 | 返回值 |
---|---|
Boolean, Number 和 String 三者 | 各自相对应的基本类型的值 |
Array,Function,Object 三者 | 其自己 |
Date | 当前时间距 1970.01.01 午夜的毫秒数 |
Math 和 Error | 没有 valueOf 方法 |
下面是实验结果:
// 数组调用 valueOf, 返回数组自己
let array = [1, 'hello', false];
console.log(array.valueOf() === array); // true
// 函数调用 valueOf, 返回函数自己
function foo(){}
console.log(foo.valueOf() === foo); // true
// 对象调用 valueOf, 返回对象自己
let obj = {
name: 'doug',
age : 22
};
console.log(obj.valueOf() === obj); // true
// 当前时间距1970年1月1日午夜的毫秒数
console.log(new Date().valueOf()); // 1551684737052
复制代码
总结: valueOf
方法能够将一个对象转换为基本数据类型, 并非每一个对象都有此方法(例如: Math 和 Error 对象). 对于布尔、数值和字符串三者的基本包装类型来讲,调用此函数返回其对应的基本类型的值; 对象调用此函数的返回值是其自己 ( 因为数组和函数本质上也是对象, 因此也返回其自身 ) .
提到 valueOf
方法就不得不想起另一个对于类型转换十分重要的方法 toString
, 下节将会讨论它.
toString()
每一个内置的对象都有此方法,是从 Object
对象继承而来的. 为使执行的结果与对象自己相符合, 大多数内置对象都重写了该函数. 常见的对象调用 toString
方法的返回值以下:
对于用户建立的对象, 返回'[object object]'. (存在一个例外, 见最后部分)
对于 Math 对象, 返回 "[object Math]":
// 自定义的对象
console.log({name: 'doug'}.toString()); // '[object Object]'
// Math 对象
console.log(Math.toString()); // '[object Math]'
复制代码
valueOf
方法获得的结果相同) .// 布尔值的包装类型的对象
console.log(new Boolean(false).toString()); // 'false'
// 数值包装类型对象的对象
console.log(new Number(3.14159).toString()); // '3.14159'
// 字符串包装类型对象的对象
console.log(new String('str').toString()); // 'str'
复制代码
// 数组
console.log([1, 'hello', false].toString()); // '1,hello,false'
复制代码
// 函数
function foo(){console.log('hello foo');}
console.log(foo.toString());
// 'function foo(){console.log('hello foo');}'
复制代码
// 正则对象
console.log(new RegExp("a+b+c").toString()); // "/a+b+c/"
// 日期对象
console.log(new Date().toString());
// Mon Mar 04 2019 17:07:54 GMT+0800 (中国标准时间)
// Error 对象
console.log(new Error('fatal error').toString());
// 'Error: fatal error'
复制代码
并不是每一个对象都有 toString()
方法, 例如经过 Object.create
函数传入null
为参数建立出来的对象, 因为它的 prototype
为 null
, 因此没有 toString
和 valueOf
方法
夲节讨论了两个重要的函数 valueOf
和 toString
, 前者返回调用者的基本类型的值, 后者能够将一个对象转化为字符串.
这两个函数将在强制类型转换过程当中起到重要的做用.
注: 包装和拆包装过程也有其余名称, 例如封装(wrap)和解封(unwrap), 只是同一个过程的不一样说法.