详细理解javascript中的强制类型转换
将值从一种类型转换为另一种类型通常称为类型转换,这是显式的情况;隐式的情况称为强制类型转换,JavaScript 中的强制类型转换总是返回标量基本类型值,如字符串、数字和布尔值。
如何理解: 类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时?
1、如果是静态语言,比如c等,所有的类型转换应该都是在编译阶段处理的吧?
2、如果是动态语言,如js等, 编译阶段会处理类型转换吗?
一、ToString
它负责处理非字符串到字符串的强制类型转换
数字的字符串化遵循通用规则,
数组的默认toString()方法经过了重新定义
Json字符串化
说明:JSON.stringify()在将JSON对象序列化为字符串时也用到了ToString,但是, JSON字符串化并非严格意义上的强制类型转换:
1、对多数简单值来说,JSON字符串化和toString()效果基本相同,只不过序列化的结果总是字符串,所有安全的JSON值都可以使用JSON.stringify()字符串化, 安全的JSON值是指能够呈现为有效JSON格式的值
JSON.stringify(); // "42"
JSON.stringify(""); // ""42""
JSON.stringify(null); // "null"
JSON.stringify(true); // "true"
2、不安全的JSON值
undefined,function,symbol和包含循环引用的对象都不符合JSON结构标准, JSON.stringify()在对象中遇到undefined,function,symbol时会自动将其忽略, 在数组中则会返回null
JSON.stringify(undefined); undefined
JSON.stringify(function(){}); // undefined
JSON.stringify([, undefined, function(){}, ]); // "[1, null, null, 4]"
JSON.stringify({a: , b: function(){}}); // "{"a": 2}""
对包含循环引用的对象执行JSON.stringify()会报错
3、如果对象中定义了toJSON方法, 那么在调用JSON.stringify之前会默认的隐式调用该方法,然后对该方法的返回值使用stringify,如果要对含有非法JSON值的对象做字符串化,可以使用toJSON来返回一个安全的JSON值,然后在stringify中对其JSON字符串化
var o = { };
var a = {
b: ,
c: o,
d: function(){}
};
// 在a中创建一个循环引用
o.e = a; // 循环引用在这里会产生错误
// JSON.stringify( a ); // 自定义的JSON序列化
a.toJSON = function() {
// 序列化仅包含b
return { b: this.b };
};
JSON.stringify( a ); // "{"b":42}"
4、JSON.stringify()中还可以传入第二个参数,这个参数是一个数组或者是函数,用来说明在序列化过程中哪些属性应该被处理:
(1)如果是一个数组,那么应该是一个包含对象中需要处理的属性的字符串数组
(2)如果是一个函数,那么它会对对象本身处理一次,然后对对象中的各个属性分别处理一次
var a = {
b: ,
c: '',
d: [, , ]
}
JSON.stringify(a, ['b', 'c']); // "{"b": 42, "c": "42"}"
JSON.stringify(a, function(k, v){
if(k !== 'c'){
return v;
}
}); // "{"b": 42, "d": [1, 2, 3]}"
这个函数中需要传递两个参数:键k和值v, 参数k在第一次调用时为undefined
二、ToNumber
将非数字值当作数字来使用
其中 true转换为1, false转换为0, undefined转换为NaN,null转换为0,对字符串的处理遵循数字常量的相关规则, 处理失败时返回NaN
1、对象如何转换为数字:先转换为对应的基本类型,然后再强制转换为数字
先检查是否有valueOf方法,如果有并且返回的是基本类型的值, 则使用该值进行强制类型转换,
如果没有valueOf()或者其返回的不是基本值,就使用toString()方法的返回值来进行强制类型转换
如果都没有valueOf()和toString(),则会产生TypeError错误
所以, 使用Object.create(null)创建的对象是不能进行强制类型转换的,例如:
var obj = Object.create(null);
obj == ; //Uncaught TypeError: Cannot convert object to primitive value
obj.valueOf = function(){return ;}
obj == ; // true
三、ToBoolean
在这一部分意识到了之前的一个误区:先说明一下:
我们知道
''==false// true
''==true; //false
但是如果字符串非空呢?
之前的错误想法是'aa'==true返回为true,但后来经过测试发现我错了,aa==true和aa==false返回的都是false
后来想了一下原因: aa在进行比较时会先转换为数字,’aa’转为数字是NaN, 所以返回的结果是false
言归正传:
JS中, 1和true,0和false可以相互转换,但它们并不是一回事:
可以被强制类型转换为false的值:
undefined
null
false
+0, -0, NaN
""
假值对象
浏览器在某些情况下在常规的js语法之外创建的一些外来的值,这些值就是假值对象,在将它们强制类型转换为布尔值时结果就是false,eg::
document.all == false// false
document.all == true// false
真值就是假值列表之外的值
目前应该只有IE里面下面代码会打印1, 在别的浏览器里面会打印2
if(document.all){
console.log();
}else{
console.log();
}
真值
真值就是假值列表之外的值
var a = 'false'
var b = ''
var c = '""' var d = Boolean(a && b && c);
d; // true
四、显示强制类型转换
1、字符串和数字之间的显式强制转换
字符串和数字之间的强制转换是通过String和Number函数进行,除此之外也有别的方式:
var a = ;
var b = a.toString(); var c = '3.14';
var d = +c; // '+'或者'-'运算符单独使用都可以将操作数转换为数值,区别在于'-'还会改变操作数的符号 b; //"42";
c; // 3.14 var c = '3.14';
var d = + +c; // 由于'++'会被当作自加运算符处理, 所以应该在中间加一个空格'+ +'
d; // 8.14
+c将其转换成了数值
2、日期转换为数字
+运算符还有一个作用是可以将日期对象转化为数字,返回的是Unix时间戳,以微秒为单位
js中作为构造函数的函数如果没有参数,可以不带括号的,eg:var a = +new Date,此外还可以使用:
var timestamp = (new Date).getTime()
或者:
var timestamp = Date.now() //(推荐的用来获取当前时间戳的方式)
3、显示解析数字字符串
解析字符串中的数字和将字符串强制类型转换为数字是有区别的
var a = '';
var b = '42px'; Number(a); //
parseInt(a); // Number(b); // NaN
parseInt(b); //
解析,顾名思义,用的是parseInt或者parseFloat,
强制转换, 使用的则是Number函数
解析需要传入的参数是字符串,如果是非字符串的话,则会先转换成字符串,解析中还可以传入第二个参数,代表转换的进制,有一个看似无厘头,实则很正确的一个例子:
parseInt(/, ); //
分析如下:
1/0 返回的是 Infinity,首先,转换成字符串 “Infinity”,也就是说,实际执行的是:
parseInt("Infinity", 19), ‘I’在19进制中代表的是18, 而’n’是没有意义的,所以解析完I以后,就返回了,所以结果是18,其他一些神奇的例子:
parseInt(0.000008); //
parseInt(0.0000008); //
parseInt(false, ); // 'f'*16 + 'a'= 15 * 16 + 10 = 250
parseInt(parseInt, ); // 'f'=15
parseInt('0x10'); //
parseInt('', ); //
parseInt(, ); //
4、显式转换为布尔值
(1)使用Boolean函数或者!!进行ToBoolean的强制类型转换
var a = '';
var b = [];
var c = {}; var d = '';
var e = ;
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
(2)显示ToBoolean的一个作用是可以在JSON序列化过程中将不安全的值返回对应的布尔值
var a = [,
function(){},
,
function(){}];
JSON.stringify(a); // "[1, null, 2, null]" JSON.stringify(a, function(k, v){
if(typeof v == 'function'){
return !!v;
}else{
return v;
}
}) // "[1, true, 2, true]"
(3)杜绝使用var b = a ? true : false(当a涉及了隐式转换为布尔值的时候)
五、隐式强制类型转换
1、字符串和数字之间的强制转换
如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话,+将进行拼接操作。如果其中一个操作数是对象(包括数组),则首先对其调用 ToPrimitive 抽象操作
var a = {
valueOf: function(){
return ;
},
toString: function(){
return ;
}
}
a + ''; // '42' 调用`valueof方法` 先转换成原始值,在将原始值转换成字符串
String(a); // '4' //String()会调用toString方法将其转换成字符串
数字到字符串的隐式转换也是类似的
var a = [], b = [];
a - b; // -1
2、布尔值和数字之间的 隐式强制转换
function onlyOne() {
var sum = ;
for(var i = ; i < arguments.length; i++){
if(arguments[i]){
sum += Number(!!arguments[i]);
}
}
return sum == ;
} var a = true;
var b = false; onlyOne(a, b); //true
onlyOne(a, a, b); //false
下面的情况下会发生布尔值的隐式强制转换
if, for, while
? :三目运算符
* 逻辑运算符||和’&&’操作符左边的操作数
3、||和&&
js中,这两个操作符返回的不一定是布尔值,它们实际上是选择两个操作数中的一个,然后将其返回。可以用它来进行代码压缩:
if(a){
foo()
}
//等价于
a&&foo()
六、宽松相等 和 严格相等
之前在区分==和===时,一直觉得说“==运算符比较值是否相等,===运算符会检查值和类型是否相等”很对, 但是这次看书上说,
==允许在相等比较中进行强制类型转换,而===不允许
觉得这种说法更加准确
1、字符串和数字之间的相等比较
无论字符串是在操作符的左边还是右边,所采取的操作都是将字符串转换成数字进行比较
2、其他类型和布尔类型之间的相等比较
会将其他类型转换成数值,然后进行比较,
var x = true, y = '';
x == y; // false
3、null 和 undefined之间的相等比较
null和undefined之间的==比较也涉及隐式强制类型转换
var a = null;
var b; a == b; //true
a == null; //true
b == null; // true a == false; //false
b == false; //false
4、对象和非对象之间的相等比较
对象和标量基本类型进行相等比较是,对象会先转换成原始值,然后进行比较
var a = ;
var b = []; a == b; //true
如果其中的对象是原始值的封装对象,那么在比较时,对象转换成原始值的过程其实也就是对象的解封装过程,但有些情况需要注意:
(1)undefined和null是没有封装对象的
(2) NaN虽然有封装对象,但是解封回来的原始值也是NaN,而NaN是不等于NaN的
var a = null;
var b = Object(a); a == b; //false var c = undefined;
var d = Object(c);
c == d; //false var e = NaN;
var f = Object(e);
e == f; //false
最后,是一些比较的易错点
'' == false; //true
false == ; //true
false == ''; //true
false == []; //true
'' == ; //true
'' == []; //true
[] == ![]; //true, 首先进行![]的隐式转换,先将`[]`转换成布尔值,为`true`,然后取反是`false`,而`[] == false`是true '' == [null]; //true, 注意`[null]`字符串话后是空字符串`''`
== '\n'; // true; 原因在于: ''或'\n'等空字符串在`ToNumber`的过程中被转换成`0`
抽象关系比较:
(1)如果比较双方都是字符串,那么进行字符串间的比较
(2)否则,会将比较双方都转换成原始值,如果转换结果中有非字符串,那么就都转换成数字进行比较
然后里面有一些比较诡异的事情:
var a = {b: };
var b = {b: };
a < b; //false
a == b; //false
a > b; //false a <= b; //true
a >= b; //true
分析如下:
首先比较双方都不是字符串,先将其都转换成原始值:
a == ‘[object Object]’, b == ‘[object Object]’;
那么不应该是 a == b吗?
因为根据规范,js中的比较是这么处理的:
a <= b; 被处理为 !(b<a), 因为 b < a的结果是false, 所以a<=b返回true
详细理解javascript中的强制类型转换的更多相关文章
- 谈 JavaScript 中的强制类型转换 (2. 应用篇)
这一部分内容是承接上一篇的, 建议先阅读谈 JavaScript 中的强制类型转换 (1. 基础篇) 前两章讨论了基本数据类型和基本包装类型的关系, 以及两个在类型转换中十分重要的方法: valueO ...
- 谈 JavaScript 中的强制类型转换 (1. 基础篇)
1. 从基本包装类型讲起 讨论基本包装类型的前提是了解基本数据类型(也可以称为原始类型, 简单数据类型等).然后通过基本数据类型调用方法的行为, 引出基本包装类型的概念和作用. 1.1 基本数据类型 ...
- 深入理解JavaScript中创建对象模式的演变(原型)
深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...
- 理解 JavaScript 中的 this
前言 理解this是我们要深入理解 JavaScript 中必不可少的一个步骤,同时只有理解了 this,你才能更加清晰地写出与自己预期一致的 JavaScript 代码. 本文是这系列的第三篇,往期 ...
- 【拾遗】理解Javascript中的Arguments
前言 最近在看JavaScript相关的知识点,看到了老外的一本Javascript For Web Developers,遇到了一个知识盲点,觉得老外写的很明白很透彻,记录下来加深印象,下面是我摘出 ...
- JS在if中的强制类型转换
JS在if中的强制类型转换 众所周知,JS在很多情况下会进行强制类型转换,其中,最常见两种是: 1.使用非严格相等进行比较,对==左边的值进行类型转换 2.在if判断时,括号内的值进行类型转换,转化为 ...
- 理解JavaScript中的原型继承(2)
两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...
- 深入理解JavaScript中的属性和特性
深入理解JavaScript中的属性和特性 JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaSc ...
- 深入理解javascript中执行环境(作用域)与作用域链
深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...
随机推荐
- P4712 「生物」能量流动
由于题面$markdown$格式,博主太懒不想一个一个改,所以题面见此:戳 Solution: 本题的贪心思路比较有意思,完全考读题... 首先,因为总的能量来源是$a[0]$,所以可以理解为总能量守 ...
- [NC2018-9-9T1]中位数
题目大意:给你一个长度为$n$的序列,要求出长度大于等于$len$的字段的中位数中最大的一个中位数 题解:可以二分答案,对于比它小的数赋成$-1$,大的赋成$1$.求前缀和,若有一段区间的和大于$0$ ...
- JavaScript中继承机制的模仿实现
首先,我们用一个经典例子来简单阐述一下ECMAScript中的继承机制. 在几何学上,实质上几何形状只有两种,即椭圆形(是圆形的)和多边形(具有一定数量的边).圆是椭圆的一种,它只有一个焦点.三角形. ...
- Windows7下的Run运行命令一览表
按住Windows键(就是左边Ctrl和Alt之间那个印windows徽标的键,简称Win键)+R,即可弹出运行对话框,在里面输入黑体字符即可运行相应程序.相比XP这次新增了不少新东西. 添加/删除程 ...
- 常用sql语句 DML语句
1.select *|字段名 from 表名 [where 条件] [order by 排序 asc|desc] [limit 限制输出 startrow,pagesize] 查询 2.insert ...
- Topcoder SRM 602 div1题解
打卡- Easy(250pts): 题目大意:rating2200及以上和2200以下的颜色是不一样的(我就是属于那个颜色比较菜的),有个人初始rating为X,然后每一场比赛他的rating如果增加 ...
- 汕头市队赛 SRM 09 A 撕书
A 撕书I-3 SRM 09 背景&&描述 琉璃在撕书. 书总共有n页,都悬浮在数轴上,第i页的位置为,上面写着一个数字. 琉璃从右往左撕书.假如看到了第i页,就把在第 ...
- Selenium2 鼠标悬停效果实现
对一些js控件,鼠标悬停的时候出发下拉层的实现 1.使用Action public void moveToElement(WebDriver driver, By locator) { Actions ...
- (转)Python 操作 Windows 粘贴板
转自: http://outofmemory.cn/code-snippet/3939/Python-operation-Windows-niantie-board Python 操作 Windows ...
- PHP使用AJax轮询实现新订单实时提醒
业务逻辑:Ajax每隔10秒钟请求一次接口,该接口会去查询数据库是否有新的订单,如果有则返回新订单的数量,后台收到声音提示,更改后台提醒数量 提醒框可链接到订单列表,后台更改完订单状态后会提醒会消失 ...