谈 JavaScript 中的强制类型转换 (1. 基础篇)
1. 从基本包装类型讲起
讨论基本包装类型的前提是了解基本数据类型(也可以称为原始类型, 简单数据类型等)。然后通过基本数据类型调用方法的行为, 引出基本包装类型的概念和作用.
1.1 基本数据类型
已经知道 JavaScript 中共有6种基本数据类型,分别是:string,number,boolean,null,undefined,symbol.
基本类型既不是对象, 也没有方法可供调用. 然而经常见到基本类型的变量调用方法的情况, 类如:
let index = 'ABCDE'.indexOf('CD');
console.log(index); // 2
上例中 'ABCDE'
是一个字符串的基本类型, 这个字符串直接调用了indexOf
方法, 将该方法的返回值保存在变量 index
中, 然后在第二行输出. 可以看到这个例子能够运行并且结果正确.
既然上面说原始类型没有方法可供调用, 那么字符串 'ABCDE'
在调用 indexOf
方法时为什么没有出错呢? 而且从运行结果来看, indexOf
这个方法确实被调用了, 那么是谁调用了这个方法并且返回了正确的结果呢? 答案就是基本包装类型. 下面引入基本包装类型的相关内容.
1.2 基本包装类型
关键词: 包装, 基本包装类型, 基本包装类型的对象
上一节讨论到其实调用方法的并不是字符串本身 , 而是基本包装类型. 下面就应该具体讨论基本包装类型的相关内容了.
为了基本类型可以正常调用方法, 后台会为这个基本类型的值自动创建一个对应的的对象, 这个对象的类型就称为基本包装类型, 然后用这个对象去调用方法, 在调用完方法之后, 这个对象就会被销毁( 用完就销毁 )了. 这个从基本数据类型生成基本包装类型对象的过程称为包装( 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;
1.3 总结
当一个基本数据类型想要调用方法时, 后台会为它生成一个临时的包装对象, 利用这个对象去调用方法, 再将方法执行的结果返回, 随后这个临时对象被销毁.
1.4 包装对象的"拆包装" box <-> unbox
从上面的内容了解到 包装(box) 是根据一个基本类型的值生成一个对应类型的对象的过程,
与这个过程大致相反, 存在一种根据包装对象生成基本类型值的过程, 可称为 拆包装 (unbox). 这个过程同样即可以由后台隐式的完成, 也可以手动的调用方法 valueOf
来做.
下一节开始讨论 valueOf
这个方法, 同时引出另外一个同样重要的方法 toString
.
2. 对象的两个重要方法 valueOf
和 toString
2.1 基本包装类型的拆包装( unbox ) 用到的 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
方法时就发生了解包装的操作.
2.2 其他对象的 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
, 下节将会讨论它.
2.3 可以将对象表示为字符串的方法 toString()
每个内置的对象都有此方法,是从 Object
对象继承而来的. 为使执行的结果与对象本身相符合, 大多数内置对象都重写了该函数. 常见的对象调用 toString
方法的返回值如下:
对于用户创建的对象, 返回'[object object]'. (存在一个例外, 见最后部分)
对于 Math 对象, 返回 "[object Math]":
// 自定义的对象
console.log({name: 'doug'}.toString()); // '[object Object]'
// Math 对象
console.log(Math.toString()); // '[object Math]'
- 对于 第一部分中提到的 3 个基本包装类型的对象:
- 对于布尔对象, 返回字符串 "true" 或 "false", 根据其对应的基本数据类型的值而定.
- 对于数值对象, 返回在指定基数下该数的字符串形式, 默认基数是 10, 即默认返回 十进制 的数用引号包裹而成的字符串.
- 对于字符串对象, 返回对应基本数据类型的字符串(和调用
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'
- 对于 函数,toString方法返回一个字符串,其中包含用于定义函数的源文本段.
// 函数
function foo(){console.log('hello foo');}
console.log(foo.toString());
// 'function foo(){console.log('hello foo');}'
- 其他对象
- 对于 RegExp 对象,返回该正则表达式的字符串.
- 对于 Date 对象, 返回表示特定时间的字符串.
- 对于 Error 对象,返回包含错误内容的字符串.
// 正则对象
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
方法
2.4 总结
夲节讨论了两个重要的函数 valueOf
和 toString
, 前者返回调用者的基本类型的值, 后者可以将一个对象转化为字符串.
这两个函数将在强制类型转换过程中起到重要的作用.
注: 包装和拆包装过程也有其他名称, 例如封装(wrap)和解封(unwrap), 只是同一个过程的不同说法.
谈 JavaScript 中的强制类型转换 (1. 基础篇)的更多相关文章
- 谈 JavaScript 中的强制类型转换 (2. 应用篇)
这一部分内容是承接上一篇的, 建议先阅读谈 JavaScript 中的强制类型转换 (1. 基础篇) 前两章讨论了基本数据类型和基本包装类型的关系, 以及两个在类型转换中十分重要的方法: valueO ...
- 详细理解javascript中的强制类型转换
将值从一种类型转换为另一种类型通常称为类型转换,这是显式的情况:隐式的情况称为强制类型转换,JavaScript 中的强制类型转换总是返回标量基本类型值,如字符串.数字和布尔值. 如何理解: 类型转换 ...
- 浅谈JavaScript中的闭包
浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...
- C#中的强制类型转换与as转换的区别
C#中的强制类型转换 例如有ClassA与ClassB两个类创建两个类的对象进行转换 1 2 ClassA a = new ClassA(); ClassB b = new ClassB(); 如果 ...
- JS在if中的强制类型转换
JS在if中的强制类型转换 众所周知,JS在很多情况下会进行强制类型转换,其中,最常见两种是: 1.使用非严格相等进行比较,对==左边的值进行类型转换 2.在if判断时,括号内的值进行类型转换,转化为 ...
- 浅谈JavaScript中的null和undefined
浅谈JavaScript中的null和undefined null null是JavaScript中的关键字,表示一个特殊值,常用来描述"空值". 对null进行typeof类型运 ...
- 浅谈JavaScript中的正则表达式(适用初学者观看)
浅谈JavaScript中的正则表达式 1.什么是正则表达式(RegExp)? 官方定义: 正则表达式是一种特殊的字符串模式,用于匹配一组字符串,就好比用模具做产品,而正则就是这个模具,定义一种规则去 ...
- JavaScript基础&实战(2)js中的强制类型转换、运算符、关系运算符、逻辑运算符、条件运算符
文章目录 1.强制类型转换Number 1.1 代码 1.2 测试结果 2.进制表示 2.1 代码 2.2 测试结果 3.强制类型转换为Boolea 3.1 代码 3.2 测试结果 4.运算符 4.1 ...
- 【总结】浅谈JavaScript中的接口
一.什么是接口 接口是面向对象JavaScript程序员的工具箱中最有用的工具之一.在设计模式中提出的可重用的面向对象设计的原则之一就是“针对接口编程而不是实现编程”,即我们所说的面向接口编程,这个概 ...
随机推荐
- hdu 1358 Period 最小循环节
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1358 分析:已知字符串,求其由最小循环节构成的前缀字符串. /*Period Time Limit: ...
- Java IO 类一览表
下表列出了大多数(非全部)按输/输出,基于字节或字符划分的 Java IO 类.
- Access时间日期函数大全
这里特别推荐WeekdayName() 函数.MonthName() 函数,将日期转换为中文星期名与月份,如"星期一"."五月"一.Date() 函数.Now( ...
- Python教你找到最心仪的对象
规则 单身妹妹到了适婚年龄,要选对象.候选男子100名,都是单身妹妹没有见过的.百人以随机顺序,从单身妹妹面前逐一经过.每当一位男子在单身妹妹面前经过时,单身妹妹要么选他为配偶,要么不选.如果选他,其 ...
- Java基础-虚拟内存之映射字节缓冲区(MappedByteBuffer)
Java基础-虚拟内存之映射字节缓冲区(MappedByteBuffer) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.映射字节缓冲区 1>.什么是虚拟内存 答:虚拟内 ...
- 浅谈fhq treap
一.简介 fhq treap 与一般的treap主要有3点不同 1.不用旋转 2.以merge和split为核心操作,通过它们的组合实现平衡树的所有操作 3.可以可持久化 二.核心操作 代码中val表 ...
- Yii 自定义模型路径
例如现有两个 Yii 项目,分别是 test1 和 test2.在 test1 中,已经有模型了,test2 直接调用 test1 中的模型,其实添加个别名,然后修改下配置即可. 先在 index.p ...
- Java 多线程(Thread) 同步(synchronized) 以及 wait, notify 相关 [实例介绍]
场景描述 有一家很大的商场,在某市有几个商品配送中心,并有几家分店,这家商场经营很多的商品,平时运营情况是这样的: 根据各分店的商品销售情况,给分店配送相应需求量的商品:并上架到分店指让的位置,供客户 ...
- 在OS X 10.9配置WebDAV服务器联合NSURLSessionUploa…
CHENYILONG Blog 在OS X 10.9配置WebDAV服务器联合NSURLSessionUploadTask实现文件上传iOS7推出的NSURLSession简化了NSURLConn ...
- HDU 3371 Connect the Cities 最小生成树(和关于sort和qsort的一些小发现)
解题报告:有n个点,然后有m条可以添加的边,然后有一个k输入,表示一开始已经有k个集合的点,每个集合的点表示现在已经是连通的了. 还是用并查集加克鲁斯卡尔.只是在输入已经连通的集合的时候,通过并查集将 ...