你不知道的的JS(中卷)-第四章

第四章 强制类型转换

1.值类型转换
JS中的强制类型转换总是返回标量基本类型值,如字符串、数字和布尔值,不会返回对象和函数。
2.抽象值操作
(1)ToString
负责处理非字符串到字符串的强制类型转化。例如null转化为“null”,undefined转化为“undefined”,true转为“true”。
对普通对象来说,除非自行定义,否则toString()返回内部属性[[class]]的值。如果对象有自己的toString()方法,字符串化时会调用该方法并使用其返回值。

将对象强制类型转换为string是通过ToPrimitive抽象操作完成的,后续详细介绍

数组的toString()方法经过了重新定义,将所有单元字符串化后再用逗号连接

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

JSON字符串化
JSON.stringify(..)在将JSON对象序列化为字符串时也用到了ToString。JSON字符串化和toString()的效果基本相同,只不过序列化的结果总是字符串。
JSON.stringify(..)在对象中遇到undefined、function、symbol时会自动将其忽略,如果在数组中则返回null,以保证单元位置不变。

JSON.stringify(undefined);//undefined
JSON.stringify(function(){});//undefined
JSON.stringify([1,undefined,function(){}]);//"[1,null,null]"
JSON.stringify({a:2,b:function(){}});//"{"a":2}"

字符串、数字、布尔值和null的JSON.stringify(..)规则与ToString相同。
如果传递给JSON.stringify(..)的对象中定义了toJSON()的方法,那么该方法会在字符串化前调用,以便将对象转换为安全的JSON值。

(2)ToNumber
ToNumber对字符串的处理失败时返回NaN,但是对以0开头的十六进制数并不按十六进制处理,而是按十进制。
对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再将其强制转换为数字。
为了将值转换为相应的基本类型值,抽象操作ToPrimitive会首先检查该值是否有valueOf()方法。如果有且返回基本类型值,就用该值进行强制类型转换,没有则用toString()的返回值来近些强制类型转换。如果valueOf()和toString()均不返回基本类型值,产生TypeError错误。

var a = {
  valueOf:function(){
    return "43";
}
};
var b = {
  toString:function(){
    return "42";
}
};
var c = [1,2];
c.toString = function(){
    return this.join("");//join将数组中的元素放入字符串
};
Number(a);//43
Number(b);//42
Number(c);//12
Number("");//0
Number([]);//0
Number(["ab"]);// NaN

true 转换为1,false转换为0,undefined转换为NaN,null转换为0.
(3)ToBoolean

  • 假值(falsy value)
    js中的值可以被分为两类:可以被强制类型转换为false的值;其他
    以下是假值:
    undefined
    null
    false
    “”
    +0、-0、NaN

    逻辑上说,假值列表以外的都叫真值。
  • 假值对象(falsy object)
    浏览器在某些特定情况下,在常规js语法基础上自己创建了一些外来值,就是“假值对象”。
    假值对象看起来和普通对象并无二致(都有属性等),但将他们强制类型转换为布尔时返回false。比如document.all
var a = new Boolean(false);
var b = new Number(0);
var c = new String("");
var d = Boolean( a && b && c);
d;//true 说明a、b、c都为true
  • 真值
    真值就是假值列表之外的值。
    []、{}、function(){}都不在假值列表中,他们都是真值
    3.显式强制类型转换
    编码时应尽可能将类型转换表达清楚。
    (1)字符串和数字之间的显示转换
    是通过String(..)和Number(..)这两个内建函数实现的,注意前面没有new,不创建封装对象。
var a = 42;
var b = String(a);//"42"
var c = "3.14";
var d = Number(c);//3.14

String遵循ToString规则,Number遵循ToNumber规则。
除了String和Number,还有其他显示转换:

var a = 42;
var b = a.toString();//"42"  不能直接42.toString()????
var c = "3.14";
var d = +c;//3.14

因为toString对42这样的基本类型值不适应,所以js引擎会自动为42创建一个封装对象,再对该对象调用toString()。
一元加法运算符(+)
上例中一元运算符“+”显式地将c转换为数字。如果一元加法运算符后面本身跟着的就是一个数字型数据,那么得到的结果就是他本身,无论这个值是正数或负数,也不论这个值是整数、小数、科学计数表示。如果这个数字型数据是八进制或是十六进制最后会用十进制的数值表示.

+ 34        // 34
+ 3.4        // 3.4
+ 34e3        // 34000
+ 34e-3        // 0.034
+ -34        // -34
+ 012        // 八进制转换成十进制  10
+ 0x12        // 十六进制转换成十进制 18

如果后面跟的不是一个数字型数据类型,就会将其进行数据类型转换,比如如果后面是布尔值,将会得到如下结果

+ true        // 1
+ false        // 0

如果后面是字符串,这个转换成数字就类似于parseInt()或者parseFloat().最终得到的结果会是一个数字或者NaN。
一元运算符+的另一个常用用途是将日期对象转换为数字,返回结果为Unix时间戳,以毫秒为单位:

var d = new Date();
+d;//

也可用如下方法获得当前时间戳:

var timesramp = +new Date();
js有特殊的语法,构造函数没有参数时可以不带()。
可能会碰到var timestamp = +new Date;但这样代码可读性降低

将日期对象转换为时间戳并非只有强制类型转换这一种方法,还有如下:

var timestamp = new Date().getTime();
getTime 方法的返回值一个数值,表示从1970年1月1日0时0分0秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。

不过最好还是使用ES5的Date.now();

var timestamp = Date.now();

建议使用Date.now()获得当前时间戳,使用new Date().getTime()获得指定时间戳
一元减法运算符(-)
一元减法运算符的转换规则类似于上面的一元加法运算符,只是对于数字取其负结果(注意负负得正的规律)。

  • 奇特的~运算符
    字位操作符“非”
    字位运算符只适用于32位整数,运算符会强制操作数使用32位格式。这是通过抽象操作ToInt32来实现的。ToInt32会先执行ToNumber强制类型转`换,再执行ToInt32.
    ~先将值强制类型转换为32位数字,再执行字位操作符“非”(对每个字位进行反转)。
    ~还有一种诠释:返回2的补码??
    ~x大致等同于-(x+1)
~42;// -(42+1) ==> -43

在-(x+1)中唯一能得到0的x值是-1.也就是说如果x为-1时,和一些数字值在一起时会返回假值0,其他情况返回真值。因为-1是一个哨位值,比如在js中indexOf方法找到子字符串就返回它的位置,否则返回-1。和indexOf一起将结果强制类型转换为真/假

var a = "Hello World!";
~a.indexOf("lo");// -4 真值
~a.indexOf("hh");//0  假值
if(~a.indexOf("hh")){...}

(2)显示解析数字字符串
解析字符串中的数字和将字符串强制类型转换为数字的返回结果都是数字。但两者之间有差别。

var a = "42";
var b = "42px";
Number(a);//42
parseInt(a);//42
Number(b);//NaN
parseInt(b);//42

解析允许字符串中有非数字字符,解析顺序从左至右,遇到非数字字符停止。转换不允许非数字字符,否则失败返回NaN。
\color{red}{ parseInt()针对的是字符串值,传递其他类型参数无效,如true,function。}

ES5之前parseInt有一个坑,如果没有第二个参数来指定转换的基数,parseInt会根据字符串的第一个字符自行决定基数。\color{red}{第一个字符如果是x或X,转为十六进制,是0转为八进制}。例如:

var hour = parseInt(selectedHour.value);
var minute = parseInt(selectedMinute.value);
console.log("Time you select is " + hour + ":" + minute);
该例子当小时为08、分钟为09结果是0:0因为8和0都不是有效的八进制数。将第二个参数设置为10可解决。
var hour = parseInt(selectedHour.value,10);

(3)显式转换为布尔值
与之前相同,Boolean(..)是显式地ToBoolean强制类型转换,但不常用。
与+类似,!显式地将强制类型转换为布尔值,它同时将真值转换为假值,所以显式地强制类型转换为布尔值的最常用的方法是!!,因为第二个!会将结果反转回原值:

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

建议使用Boolean和!!进行显式强制类型转换。
4.隐式强制类型转换
即隐蔽的强制类型转换。
(1)隐式地简化
字符串和数字之间的隐式强制类型转换

var a = [1,2];
var b = [3,4];
a+b;// "1,23,4"

如果某个操作数是字符串或能够通过以下步骤转换为字符串,+将进行拼接操作。如果一个操作数是对象(包括数组),则首先对其调用ToPrimitive抽象操作,再调用[[DefaultValue]],以数字作为上下文。

简单来说,如果有一个操作数是字符串,则执行字符串拼接,否则执行数字加法。

a+""这样的隐式转换很常见,但和String有细微差别。a+""会对a调用valueOf方法,然后通过ToString抽象操作返回字符串,String(a)会直接调用ToString().
(2) || 和&&(操作数选择器)
|| 和&&返回的不一定是布尔值,而是两个操作数中的一个
对||来说如果第一个操作数为真,则返回第一个操作数,否则返回第二个操作数。
&&的第一个操作数为真则返回第二个操作数,反之返回第一个操作数。

a||b大致等于a?a:b
a&&b大致等于a?b:a

||是常见的“空值合并运算符”:a = a ||"hello"
&&是“守护运算符”:a&& foo()即只有在条件判断a通过时才会执行函数foo
(3)符号的强制类型转换
ES6允许从符号到字符串的显式强制类型转换,隐式强制类型转换会产生错误。

var s1 = Symbol("cool");
String(s1);//Symbol(cool)
var s2 = Symbol("not cool");
s2 +"";//TypeError

5.宽松相等和严格相等
==和===
==允许在比较中进行强制类型转换,===不允许
(1)抽象相等
注意:

  • NaN不等于NaN
  • +0不等于-0
    字符串和数字之间的比较:将字符串Number化再比较
    其他类型和布尔类型的比较:将布尔型Number化再比较
var a = "42";
var b = true;
a==b;//false

null和undefined之间的相等比较:
-\color{red}{ null == undefined;//true}
所以判断是否是null或undefined时可以直接if(a == null){...}
(6)抽象关系比较

  • 比较双方先调用ToPrimitive,如果结果出现非字符串,就根据ToNumber规则将双方强制类型转换为数字来比较。
var a = [42];
var b = ["43"];
a<b;//true
a>b;//false
  • 双方都是字符串,按字母顺序比较:
var a = ["42"];
var b = ["043"];
a<b;//false

看一个奇怪的例子:

var a = { b:42 };
var b = { b:43 };
a < b;//false
a ==b;//false
a > b;//false
a <= b;//true
a >= b;//true

根据规范,a <= b被处理为b<a,然后将结果反转,因为b<a返回false,所以a<=b为true。\color{red}{在js里“<=”被处理为“不大于”。}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 值类型转换将值从一种类型转换为另一种类型通常称为类型转换,这是显示的情况;隐式的情况称为强制类型转换。JavaSc...
    xpwei阅读 3,519评论 0 5
  • 强制类型转换 值类型转换 将值从一种类型转换为另一种类型通常称为类型转换(type casting),这是显示的情...
    风声233阅读 137评论 0 1
  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,114评论 0 13
  • 官方中文版原文链接 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大...
    HetfieldJoe阅读 2,499评论 1 17
  • 所谓,精神。乃是指一个人区别于动物生存在世其思考与行为的准则。它犹如一条无形的线,在生存、生活、发展的方方面面引...
    小乌龟呀阅读 509评论 0 0