zoukankan      html  css  js  c++  java
  • JS 中类型和类型转换

      类型  

      Js中的类型是针对值来说,而不是针对变量,值就是所谓的42, 'abc', false 等能用js 操作的数据。在js 中说某某某是什么数据类型,其实是说的这些值是什么类型。值呢?有7种数据类型: number, string, boolean, null, undefined, object, symbol. 42 就是number 类型了。怎么判断出来的,最简单的办法就是typeof 操作符了,它返回一个字符串来表示类型

    console.log(typeof 42); // 'number'

      这时,你可能想到,不对啊,以前也用typeof 操作过变量啊?也返回类型了啊。

    let a = 42
    console.log(typeof a); // 'number'

      其实这里仍然是对值进行的类型判断,因为你不能说变量a就是number 类型,因为 js 是弱类型语言,完全可以把重新赋值为一个变量。

    let a = 42
    console.log(typeof a); // 'number'
    a = 'str';
    console.log(typeof a); // 'string'

      这时,你就说不出变量a 是什么类型了。在js 中,如果对一个变量进行typeof 操作,一定是对当时变量所指向或所包含的值进行typeof 操作,变量没有类型,值有。

      但使用typeof 进行类型判断的时候,它有一个bug, typeof null 返回的是'object' 而不是'null'.  null 值是'null' 类型的唯一值,它竟然返回了'object'. 这时,如果你就想用typeof 操作符来判断一个值是不是null, 你就要再加一个判断了。只有对象和null 执行typeof 操作时候,才返回'object', 只要找出二者的不同就可以了,很简单,所有对象都是真值,null 是假值,!a 是true 就可以区分了。所以使用typeof 判断null的时候,如下

    let a = null;
    (!a && typeof a === 'object') // true

      其时 typeof 还一个小问题,就是typeof 一个函数的时候,返回的是一个'funciton', 

    let a = () => {};
    console.log(typeof a); // 'function'

      这可能让人产生误会,误以为'funciton' 也是一种数据类型,其实并不是,所有的函数都是对象,不过这也提供了一个判断函数的简便方法。

      当然,typeof 操作符还有一个好处,就是如果一个变量没有声明的话,执行typeof 操作,不会报错,而是返回undefined, 这就是提供了一个安全的操作,因为,如果你直接引用一个没有声明的变量就会报错。有时,你想判断一个变量存不存在,如要写a == undefined, 完了,报错了,引用了一个没有声明的变量a. 如果使用 typeof a == undefined, 没有问题。

      Number类型

      JS 依据IEEE 754标准,使用64-bit的浮点数来表示数字。尽管没有整数类型,但它也能准确地表示-2^53 ~2^53-1个整数,整数就是没有小数,或小数后面全是0,如42 或42.00。如果使用整数超过这个范围,可能要损失精度,就是计算不准确。 默认情况下, 数值的输出方式,都是10进制,并且会把末尾的0 去掉。

    var a = 42.300;
    var b = 42.0;
    console.log(a); // '42.3'
    console.log(b) // 42

      但是如果一个数特别大或特别小的话,它会以指数形式进行输出,在chrome 浏览器中,只要整数大于20次方,或小数后面有7位,就会转化为指数

    var a = 5000000000000000000000;
    var b = 0.0000005;
    console.log(a); // 5e+21
    console.log(b) // 5e-7

      JS做算数运算时, 上溢 , 下溢和除0 并不会报错。如果上溢,就是计算结果超出了JS能表示的最大值,它会返回Infinity, -Infinity; 如果下溢,就是计算结果无限接近0 ,超出了JS 能表示的最小值,它会返回0 或-0. 除0,简单返回Infinity, -Infinity, 当然0/0没有意义,返回NaN

    Number.MIN_VALUE/2 // 0
    Number.MAX_VALUE * 2 // Infinity
    1/0 // Infinity
    0/0  // NaN

      现实中的实数是无限的,而JS只能表示有限个实数,由于内存等原因。也就是说,当我们使用JS中的实数时,这些数只是现实中实数的近似值。IEEE-754 浮点数使用的是二进制表示法,它能准确地表示1/2, 1/8 等,但不能准确表示1/10, 也就是0.1。数值比较时就会出问题,0.1 + 0.2 === 0.3 为false. 有没有办法使0.1 + 0.2 和0. 3 比较时,显示相等呢?一种是办法是设置一个容错的边界, 当小于这个值 的时候,就表示这两个值相等了,这个值在js 中是2的-52 次方Math.pow(2,-52),Es6 专门定义了这个常量Number.EPSILON,  只要两者进行比较小于这个数,就证明这两个值相等。

    // 无限接近相等
    function isCloseEnoughToEqual(n1,n2) {
        return Math.abs( n1 - n2 ) < Number.EPSILON;
    }
    let a = 0.1 + 0.2;
    let b = 0.3 
    console.log(isCloseEnoughToEqual(a, b)) // true

      计算整数没有问题,这个整数也是有边界的, 对于double双精度浮点数,用 1 位表示符号,用 11 位表示指数,52 位表示尾数,所以浮点数表示的最大整数是2的53次方-1(Math.pow(2, 53))最小整数是-2的53次方, 即[-2^53, 2^53 - 1], 超出了这个边界,计算也是不准确

    let a = 9007199254740992; // 2^53
    let b = a + 1;
    console.log(a == b) // true

      a + 1 和a 是相等的,所以出现了问题了。为些,Es6 定义了安全数值 的字面量和判断安全数值的方法 Number.MAX_SAFE_INTEGER, Number.MIN_SFTE_INTEGER 

    以及Number.isSafeInteger(); 当然也提供了判断整数的方法Number.isInteger(); 

    let a = 9007199254740991; // 2^53 -1
    let b = a + 1;
    console.log(Number.isSafeInteger(a)) // true
    console.log(Number.isSafeInteger(b)) // false
    console.log(Number.isInteger(a)) // true
    console.log(Number.isInteger(b)) // true

      a 和 b 都是整数,但a 是安全整数,b 不是

      Number 类型中的特殊值--- NaN。当我们执行算术操作的时候,这个操作并没有成功,按理说,它应该报错,但是Js 并不会,而是返回特殊的值NaN。

    let a = 2 / 'foo';
    console.log(a ) // NaN

      我们怎么判断值是NaN, 可能最简单的就是 它=== NaN, 但是很不幸,NaN 和它本身并不相等,NaN === NaN 返回的是false. 其次还有一个isNaN 的函数来判断NaN, 但是它也有点问题,传给isNaN的参数,首先会进行自动类型转化,看能不能转化成数字,如能就是返回true, 如果不能返回false.

    let a = 2 / 'foo';
    console.log(isNaN(a)) // true
    let b = 'foo';
    console.log(isNaN(b)) // true

      可以看到 b 不是NaN, 但是isNaN 仍然返回了true, 也就是说,即使isNaN() 返回了true, 我们也不能确定这个值是不是NaN。为了解决这个问题,ES6增加了Number.isNaN(), 如果值为NaN, 它返回ture ,否则返回false.

    let a = 2 / 'foo';
    console.log(Number.isNaN(a)) // true
    let b = 'foo';
    console.log(Number.isNaN(b)) // false

      Number.isNaN() 是怎么判断的呢?首先它判断这个值是不是number 类型,NaN 作为值,它其实是number 类型

    let a = 2 / 'foo';
    console.log(typeof a === 'number') // true

      其次才是调用isNaN 方法来判断。

       Number 类型中的特殊值--- +0 和-0.  Js 中的0 有两个,+0 和-0. 除了字面量的方式可以得到-0 外,使用* 或 / 操作符,也可以得到,但+ - 操作符不能。

    let a = -1 * 0;
    let b = 0 / -1;
    console.log(a, b) // -0

      有两个0 也不要紧,但是它有几个奇怪的行为

        1, +0 和 -0 使用== 或=== 比较的时候,是相等。

    let a = -0;
    let b = 0 
    console.log(a === b) // true

      其实它两个是不相等,所以ES6 增加了Object.is() 方法,来判断它两个并不相等。

      2, -0 转化成字符串的时候,输出的结果是‘0’ ,而是不-0. JSON.stringify(-0) 也是得到'0'

    let a = -0;
    console.log(a.toString()) // '0'
    console.log(JSON.stringify(a)) // '0'

       然而当一个'-0' 字符串转化数字的时候,它返回的是-0, 正常的。

    +"-0"; // -0
    Number( "-0" ); // -0
    JSON.parse( "-0" ); // -0

      null 和undefined

      null 是一个关键字,不能用它作为变量名,更不能给它赋值,但undefined 不是,它可以是一个标示符,可以给它赋值,当然,这仅限在非严格模式,如果是严格模式的话,会报错。

     var undefined = 2;
     "use strict";
    var undefined = 2; // 报错

      由于undefined 可以被改写,有一个void 操作符,它后面可以是任何表达式,返回的值是undefined, 不过一般使用 void 0, 这样undefined 就不会被改写了。

    console.log(void 0 === undefined) // true

       对象(引用类型)

      如果一个值执行typeof 操作返回 ‘object’,那么它就是引用类型,其实,还可以进行细分,它具体是对象下面的哪一个类型,数组,还是函数,使用的是Object.prototype.toString.call(值)

    let a = [1,2];
    let b = function(){};
    let c = new Date();
    
    console.log(Object.prototype.toString.call(a)) // '[object Array]' Array 类型
    console.log(Object.prototype.toString.call(b)) // '[object Function]' Function 类型
    console.log(Object.prototype.toString.call(c)) // '[object Date]' Date 类型

      那对基本数据类型调用toString() 方法呢?它返回基本类型的包装类型,甚至对null, undefined 都可以调用, 返回值如下

    console.log(Object.prototype.toString.call(1)) // '[object Number]'
    console.log(Object.prototype.toString.call('abc')) // '[object String]'
    console.log(Object.prototype.toString.call(false)) // '[object Boolean]' 
    console.log(Object.prototype.toString.call(null)) // '[object Null]' 
    console.log(Object.prototype.toString.call(undefined)) // '[object Undefined]' 
    console.log(Object.prototype.toString.call(Symbol())) // '[object Symbol]' 

      数组类型

      1, 删除数组元素的时候,不要用delete, 使用delete 可以把数组中的元素及元素在数组中的位置(slot)删除掉, 但不用更新数组的length 长度。

    let a = [1, 2];
    delete a[0];
    console.log(a);
    console.log(a.length)

      下图是chrome 浏览器的返回值。

      可以使用splice() 进行删除

      2, 不要创建稀疏数组。当创建稀疏数组的时候,两个不相邻元素之间的slot 是不存在的,a[0] 和a[2] 之间并没有slot, 尽管获取到它的值是undefined

    var a = [ ];
    a[0] = 1;
    // 没有 a[1] slot
    a[2] = 3;
    a[1]; // undefined
    a.length; // 3

      当我们使用构造函数创建数组时, 给它传递一个数字,它就会创建length 为参数的数组,这个数组其实也是一个稀疏数组. 比如,你传递了一个3,那么创建的数组的length 为3,我们以为这个数组有了3个slot, 值为undefined, 其实它没有任何的slot. 当你使用forEach 的时候,什么都没有输出

    arr.forEach((item, index) => {
        console.log(item);
        console.log(index);
    })

      3, 尽管数组是对象,但千万不要作为对象进行使用,比如 a["name"] = 'Sam', 尤其是属性是数字字符串的时候,这时它会把字符串转化为数字,作为数组的元素

    let a = [];
    a['5'] = 5;
    console.log(a.length) // 6

       类型转化

      首先要记住的是,Js中的类型转化,它得到的结果永远是基本类型,也就可以分为两种情况,基本类型和基本类型之间的转化,引用类型转化为基本类型,不可能出现基本类型转化为引用类型。类型转化出现最多就是转化成number, string, 和boolean. 这就是说,我们在学习类型转化的时候,也要分为两种情况去学,比如转化成string,  基本类型是怎么转化string的,引用类型是怎么转化成string的。

      

      

      

  • 相关阅读:
    HDU 5818 Joint Stacks
    HDU 5816 Hearthstone
    HDU 5812 Distance
    HDU 5807 Keep In Touch
    HDU 5798 Stabilization
    HDU 5543 Pick The Sticks
    Light OJ 1393 Crazy Calendar (尼姆博弈)
    NEFU 2016省赛演练一 I题 (模拟题)
    NEFU 2016省赛演练一 F题 (高精度加法)
    NEFU 2016省赛演练一 B题(递推)
  • 原文地址:https://www.cnblogs.com/SamWeb/p/10984301.html
Copyright © 2011-2022 走看看