搜文章
推荐 原创 视频 Java开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发
Lambda在线 > 前端JavaScript > [S3-E455]javascript中的强制类型转换

[S3-E455]javascript中的强制类型转换

前端JavaScript 2017-11-30


将值从一种类型转换为另一种类型通常称为类型转换,这是显式的情况;隐式的情况称为强制类型转换

JavaScript 中的强制类型转换总是返回标量基本类型值,如字符串、数字和布尔值


如何理解: 类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时?

1. 如果是静态语言,比如c等,所有的类型转换应该都是在编译阶段处理的吧?

2. 如果是动态语言,如js等, 编译阶段会处理类型转换吗?


ToString

它负责处理非字符串到字符串的强制类型转换

数字的字符串化遵循通用规则,

数组的默认toString()方法经过了重新定义


Json字符串化

说明

`JSON.stringify()`在将JSON对象序列化为字符串时也用到了`ToString`,

但是, JSON字符串化并非严格意义上的强制类型转换:


1. 对多数简单值来说,JSON字符串话和`toString()`效果基本相同,只不过序列化的结果总是字符串, 所有** 安全的SJON值 **都可以使用`JSON.stringify()`字符串化, 安全的JSON值是指能够呈现为有效JSON格式的值

    JSON.stringify(42); // "42"

    JSON.stringify("42"); // ""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([1, undefined, function(){}, 4]); // "[1, null, null, 4]"

    JSON.stringify({a: 2, b: function(){}}); // "{"a": 2}""

对包含循环引用的对象执行JSON.stringify()会报错


3. 如果对象中定义了toJSON方法, 那么在调用JSON.stringify之前会默认的隐式调用该方法,然后对该方法的返回值使用stringify,

如果要对含有非法JSON值的对象做字符串化,可以使用toJSON来返回一个安全的JSON值,然后在stringify中对其JSON字符串化

    var o = { };


    var a = {

        b: 42,

        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()中还可以传入第二个参数,这个参数是一个数组或者是函数,用来说明在序列化过程中哪些属性应该被处理

* 如果是一个数组,那么应该是一个包含对象中需要处理的属性的字符串数组

* 如果是一个函数,那么它会对对象本身处理一次,然后对对象中的各个属性分别处理一次,

    var a = {

        b: 42,

        c: '42',

        d: [1, 2, 3]

    }

    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)创建的对象是不能进行强制类型转换的, eg:

var obj = Object.create(null);

obj == 1; //Uncaught TypeError: Cannot convert object to primitive value

obj.valueOf = function(){return 1;}

obj == 1; // 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(1);

}else{

    console.log(2);

}

真值

真值就是假值列表之外的值

var a = 'false'

var b = '0'

var c = '""'


var d = Boolean(a && b && c);

d; // true


显示强制类型转换


字符串和数字之间的显式强制转换


字符串和数字之间的强制转换是通过String和Number函数进行,除此之外也有别的方式:

var a = 42;

var b = a.toString();


var c = '3.14';

var d = +c; // '+'或者'-'运算符单独使用都可以将操作数转换为数值,区别在于'-'还会改变操作数的符号


b; //"42";

c; // 3.14


var c = '3.14';

var d = 5+ +c; // 由于'++'会被当作自加运算符处理, 所以应该在中间加一个空格'+ +'

d; // 8.14


日期转换为数字


+运算符还有一个作用是可以将日期对象转化为数字,返回的是Unix时间戳,以微秒为单位


js中作为构造函数的函数如果没有参数,可以不带括号的,eg: var a = +new Date, 此外还可以使用:

var timestamp = (new Date).getTime()

或者:

var timestamp = Date.now() //(推荐的用来获取当前时间戳的方式)


位运算相关


字位运算只适用与32位整数,运算符会强制操作数使用32位格式。

~x大致等同于-(x+1),只有当x=-1时, -(x+1)才等于0,


字位截除

可以使用~~来截除数字的小数部分,但这不同与Math.floor, |也可以,但是考虑到优先级,更常用的是~~;

Math.floor(-49.6); // -50

~~-49.6; // -49


显示解析数字字符串


解析字符串中的数字和将字符串强制类型转换为数字是有区别的

    var a = '42';

    var b = '42px';


    Number(a); //42

    parseInt(a); //42


    Number(b); // NaN

    parseInt(b); // 42


解析,顾名思义,用的是parseInt或者parseFloat,

强制转换, 使用的则是Number函数

解析需要传入的参数是字符串,如果是非字符串的话,则会先转换成字符串,解析中还可以传入第二个参数,代表转换的进制,有一个看似无厘头,实则很正确的一个例子:

parseInt(1/0, 19); // 18


分析如下: 

1/0 返回的是 Infinity, 首先,转换成字符串 “Infinity”,也就是说,实际执行的是:

parseInt("Infinity", 19), ‘I’在19进制中代表的是18, 而’n’是没有意义的,所以解析完I以后,就返回了,所以结果是18,其他一些神奇的例子:

parseInt(0.000008);  // 0

parseInt(0.0000008); // 8

parseInt(false, 16); // 'f'*16 + 'a'= 15 * 16 + 10 = 250

parseInt(parseInt, 16); // 'f'=15

parseInt('0x10'); // 16

parseInt('103', 2); //2

parseInt(78, 8); // 8


显式转换为布尔值


1、使用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


2、显示ToBoolean的一个作用是可以在JSON序列化过程中将不安全的值返回对应的布尔值

var a = [1, 

        function(){},

        2,

        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涉及了隐式转换为布尔值的时候)


隐式强制类型转换


字符串和数字之间的强制转换


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

    var a = {

        valueOf: function(){

            return 42;

        },

        toString: function(){

            return 4;

        }

    }

    a + ''; // '42' 调用`valueof方法` 先转换成原始值,在将原始值转换成字符串

    String(a); // '4' //String()会调用toString方法将其转换成字符串


数字到字符串的隐式转换也是类似的

    var a = [1], b = [2];

    a - b; // -1


布尔值和数字之间的 隐式强制转换


function onlyOne() {

    var sum = 0;

    for(var i = 0; i < arguments.length; i++){

        if(arguments[i]){

            sum += Number(!!arguments[i]);

        }

    }

    return sum == 1;

}


var a = true;

var b = false;


onlyOne(a, b); //true

onlyOne(a, a, b); //false


下面的情况下会发生布尔值的隐式强制转换

if, for, while

? :三目运算符

* 逻辑运算符||和’&&’操作符左边的操作数


||和&&


js中,这两个操作符返回的不一定是布尔值,它们实际上是选择两个操作数中的一个,然后将其返回

可以用它来进行代码压缩:

if(a){

    foo()

}


a&&foo()


宽松相等 和 严格相等


之前在区分==和===时,一直觉得说“==运算符比较值是否相等,===运算符会检查值和类型是否相等”很对, 但是这次看书上说,


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

觉得这种说法更加准确

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

无论字符串是在操作符的左边还是右边,所采取的操作都是将字符串转换成数字进行比较


2、其他类型和布尔类型之间的相等比较

会将其他类型转换成数值,然后进行比较,

eg:

var x = true, y = '42';

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 = 42;

var b = [42];


a == b;  //true


如果其中的对象是原始值的封装对象,那么在比较时,对象转换成原始值的过程其实也就是对象的解封装过程,但有些情况需要注意:

1. undefined和null是没有封装对象的

2. NaN虽然有封装对象,但是解封回来的原始值也是NaN,而NaN是不等于NaN的

eg:

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


最后,是一些比较的易错点

'0' == false; //true

false == 0; //true

false == ''; //true

false == []; //true

'' == 0; //true

'' == []; //true

[] == ![]; //true, 首先进行![]的隐式转换,先将`[]`转换成布尔值,为`true`,然后取反是`false`,而`[] == false`是true


'' == [null]; //true, 注意`[null]`字符串话后是空字符串`''`

0 == '\n'; // true; 原因在于: ''或'\n'等空字符串在`ToNumber`的过程中被转换成`0`

抽象关系比较:


1、如果比较双方都是字符串,那么进行字符串间的比较

2、否则,会将比较双方都转换成原始值,如果转换结果中有非字符串,那么就都转换成数字进行比较

然后里面有一些比较诡异的事情:


var a = {b: 42};

var b = {b: 43};

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



>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

更多文章点击  查看(或在导航菜单中查看

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

====================

版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《[S3-E455]javascript中的强制类型转换》的版权归原作者「前端JavaScript」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

关注前端JavaScript微信公众号

前端JavaScript微信公众号:qdjavascript

前端JavaScript

手机扫描上方二维码即可关注前端JavaScript微信公众号

前端JavaScript最新文章

精品公众号随机推荐

上一篇 >>

TCP协议【53】