最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • [概念细节] javaScript中的数据类型

    正文概述 掘金(NeverMore奈文摩尔)   2020-12-11   524

    数据类型

    系列开篇

    面试题

    • js中的基本类型可以列举下吗
    • 基本类型和引用类型区别
    • 强制类型转换的一些手写问题
    • 数据类型判断方法
    • 什么时候自动转换为string类型
    • Object.is和===的区别
    • ...... 这个方面可以问很多问题,注重对细节的把握,编程是个细活

    javascript是弱类型语言

    JavaScript 是一种弱类型或者说动态语言

    这意味着你不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。这也意味着你可以使用同一个变量保存不同类型的数据

    那么声明变量的时候并没有预先确定的类型。也就是说变量当前的类型由其值所决定,计算时能发生隐式类型转换,或者叫强制类型转换

    最新的 ECMAScript 标准定义了 8 种数据类型:

    7 种 原始类型 | 基本数据类型 | 值类型 |

    • Boolean
    • Null
    • Undefined
    • Number
    • BigInt
    • String
    • Symbol

    引用数据类型 | 对象类型

    • Object

    基本数据类型详细查看

    Boolean

    这个没啥好说

    Number

    NaN 是一种特殊的Number类型 下面列举什么时候返回NaN:

    1. 无穷大除以无穷大
    2. 给任意负数做开方运算
    3. 算数运算符与不是数字或无法转换为数字的操作数一起使用
    4. 字符串解析成数字

    例子

    Infinity / Infinity; // 无穷大除以无穷大
    Math.sqrt( - 1); // 给任意负数做开方运算
    
    // 算数运算符与不是数字或无法转换为数字的操作数一起使用
    // 注意没有 '+' 运算符
    'a' - 1;
    'a' * 1;
    'a' / 1;
    
    // 字符串解析成数字
    parseInt('a'); 
    parseFloat('a');
    Number('a');
    'abc' - 1 
    undefined + 1
    
    //一元运算符(注意点)
    + 'abc' // NaN
    - 'abc' // NaN
    undefined++
    

    String

    toString()

    toString() 可以将数据都转为字符串但是nullundefined不可以转换。例如

    console.log(null.toString())
    //报错 TypeError: Cannot read property 'toString' of null
    
    console.log(undefined.toString())
    //报错 TypeError: Cannot read property 'toString' of undefined
    

    toString()第一个参数,代表进制

    • 二进制:.toString(2);
    • 八进制:.toString(8);
    • 十进制:.toString(10);
    • 十六进制:.toString(16);
    let a = 10
    a.toString(2) //1010
    

    String()

    String()可以将null和undefined转换为字符串,但是没法转进制字符串

    console.log(String(null));
    // null
    
    console.log(String(undefined));
    // undefined
    

    string类型转换开发过程中可能出错的点

    let obj = {
        width: '100'
    };
    obj.width + 20 // “10020"
    

    预期输出结果120 实际输出结果10020

    Null

    虽然 typeof null 会输出 object 但null不是对象

    Undefined

    这个也没啥好说 '未定义'

    Symbol

    Symbol实例是唯一且不可改变的。也就是说,Symbol 生成一个全局唯一的值可以保证不会与其他属性名产生冲突。

    Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...in、for...of循环中,也不会Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。

    但是,它也不是私有属性,有一个Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

    Symbol("foo") !== Symbol("foo")
    
    const foo = Symbol()
    const bar = Symbol()
    typeof foo === "symbol"
    typeof bar === "symbol"
    
    let obj = {}
    obj[foo] = "foo"
    obj[bar] = "bar"
    
    JSON.stringify(obj) // {}
    Object.keys(obj) // []
    Object.getOwnPropertyNames(obj) // []
    Object.getOwnPropertySymbols(obj) // [ foo, bar ]
    

    详细内容请查看 MDN

    另外有个妙用 利用Symbol消除魔术字符串

    Bigint

    BigInt是一种新的数据类型,用于当整数值大于Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数id,等等,而不需要使用库。

    为什么需要BigInt?

    在JS中,所有的数字都以双精度64位浮点格式表示,那这会带来什么问题呢? 这导致JS中的Number无法精确表示非常大的整数,它会将非常大的整数四舍五入,确切地说,JS中的Number类型只能安全地表示

    -9007199254740991(-(2^53-1)) 和 9007199254740991((2^53-1))

    任何超出此范围的整数值都可能失去精度。

    console.log(99999999999999999);  //=>1000000000000000000
    
    // 同时也会有一定的安全性问题:
    9007199254740992 === 9007199254740993;    // 居然是true!
    

    如何创建并使用BigInt?

    要创建BigInt,只需要在数字 末尾追加n 即可。

    console.log( 9007199254740995n );    // → 9007199254740995n	
    console.log( 9007199254740995 );     // → 9007199254740996
    

    另一种创建BigInt的方法是用BigInt()构造函数

    BigInt("9007199254740995");    // → 9007199254740995n
    

    简单使用如下:

    10n + 20n;           // → 30n	
    10n - 20n;           // → -10n	
    +10n;                // → TypeError: Cannot convert a BigInt value to a number	
    
    -10n;                // → -10n	
    10n * 20n;           // → 200n	
    20n / 10n;           // → 2n	
    23n % 10n;           // → 3n	
    10n ** 3n;           // → 1000n	
    
    const x = 10n;	
    ++x;                 // → 11n	
    --x;                 // → 9n
    console.log(typeof x);   //"bigint"
    

    值得警惕的点

    • BigInt不支持一元加号运算符, 这可能是某些程序可能依赖于 + 始终生成 Number 的不变量,或者抛出异常。
    • 因为隐式类型转换可能丢失信息,所以不允许在bigint和 Number 之间进行混合操作。当混合使用大整数和浮点数时,结果值可能无法由BigInt或Number精确表示。
    10 + 10n;    // TypeError
    
    • 不能将BigInt传递给Web api和内置的 JS 函数,这些函数需要一个 Number 类型的数字。尝试这样做会报TypeError错误。
    Math.max(2n, 4n, 6n);    // → TypeError
    
    • 当 Boolean 类型与 BigInt 类型相遇时,BigInt的处理方式与Number类似,换句话说,只要不是0n,BigInt就被视为truthy的值。
    if(0n){
      //条件判断为false
    }
    if(3n){
      //条件为true
    }
    
    • 元素都为BigInt的数组可以进行sort
    • BigInt可以正常地进行位运算,如|、&、<<、>>和^

    引用数据类型|对象类型 列举

    • 普通对象-Object
    • 数组对象-Array
    • 日期对象-Date
    • 正则对象-RegExp
    • 数学函数-Math
    • 函数对象-Function
    • Set/Map/WeakSet/WeakMap

    基本类型和引用类型区别

    不可变与可变

    对象类型也叫引用类型arrayfunction是对象的子类型。对象在逻辑上是属性的无序集合,是存放各种值的容器。对象值存储的是引用地址,所以和基本类型值不可变的特性不同,对象值是可变的,我们可为为引用类型添加属性和方法,也可以删除其属性和方法

    存放位置不同

    • 基本类型值 => 栈内存
    • 引用类型 => 同时在栈内存和堆内存

    let name = 'hello'
    let age = 25;
    
    [栈区]
    
    name | hello
    age | 25
    

    栈区包括了 变量的标识符变量的值

    javascript和其他语言不同,其不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间那我们操作对象的引用,所以引用类型的值是按引用访问的

    准确地说,引用类型的存储需要内存的栈内存和堆内存共同完成栈区内存保存变量标识符指向堆内存中该对象的指针,也可以说是该对象在堆内存的地址

    栈区                                       堆区
    person1 | 堆内存地址1(指针)       --->       obj1
    

    强制类型转换 | 隐式类型转换规则

    对象 — 原始值转换

    当对象相加 obj1 + obj2,相减 obj1 - obj2,或者使用 alert(obj) 打印时会发生什么? 在这种情况下,对象会被自动转换为原始值,然后执行操作。

    ToPrimitive对原始类型不发生转换处理只针对引用类型(object)的,其目的是将引用类型(object)转换为非对象类型,也就是原始类型。

    ToPrimitive 运算符接受一个值,和一个可选的期望类型作参数。对象到原始值的转换,是由许多期望以原始值作为值的内建函数和运算符自动调用的。

    转换后的结果原始类型是由期望类型决定的,期望类型其实就是我们传递的type。直接看下面比较清楚。ToPrimitive方法大概长这么个样子具体如下。

    /**
    * @obj 需要转换的对象
    * @type 期望转换为的原始数据类型,可选
    */
    ToPrimitive(obj,type)
    
    • type为string
      • 先调用obj的toString方法,如果为原始值,则return,否则第2步
      • 调用obj的valueOf方法,如果为原始值,则return,否则第3步
      • 抛出TypeError 异常
    • type为number
      • 调用obj的valueOf方法,如果为原始值,则返回,否则下第2步
      • 调用obj的toString方法,如果为原始值,则return,否则第3步
      • 抛出TypeError 异常
    • type参数为空
      • 该对象为Date,则type被设置为String
      • 否则,type被设置为Number

    toString (Object.prototype.toString())

    toString 方法返回一个表示该对象的字符串

    '1'.toString()为什么可以调用? 其实在这个语句运行的过程中做了这样几件事情:

    • let s = new Object('1');
    • s.toString();
    • s = null;
    1. 第一步: 创建Object类实例。注意为什么不是String ? 由于Symbol和BigInt的出现,对它们调用new都会报错,目前ES6规范也不建议用new来创建基本类型的包装类
    2. 第二步: 调用实例方法。
    3. 第三步: 执行完方法立即销毁这个实例。

    整个过程体现了基本包装类型的性质,而基本包装类型恰恰属于基本数据类型,包括Boolean, NumberString

    valueOf (Object.prototype.valueOf())

    valueOf 方法返回指定对象的原始值

    JavaScript 调用 valueOf() 方法用来把对象转换成原始类型的值(数值、字符串和布尔值)。但是我们很少需要自己调用此函数,valueOf 方法一般都会被 JavaScript 自动调用。

    let str = new String('123');
    console.log(str.valueOf());
    // 123 字符串
    
    var num = new Number(123);
    console.log(num.valueOf());
    // 123 数字类型
    
    let date = newDate();
    console.log(date.valueOf());
    //1526990889729
    
    let bool = newBoolean('123');
    console.log(bool.valueOf());
    //true
    
    let obj = newObject({
        valueOf: () => {
            return 1
        }
    }) 
    console.log(obj.valueOf());  
    // 1
    

    Number

    对象这里要先转换为原始值,调用ToPrimitive转换,type指定为number了,继续回到ToPrimitive进行转换(看ToPrimitive)。

    • null 转换为 0
    • undefined 转换为 NaN
    • true 转换为 1,false 转换为 0
    • 字符串转换时遵循数字常量规则,转换失败返回 NaN

    String

    对象这里要先转换为原始值,调用ToPrimitive转换,type就指定为string了,继续回到ToPrimitive进行转换(看ToPrimitive)。

    • null 转换为 'null'
    • undefined 转换为 'undefined'
    • true 转换为 'true',false 转换为 'false'
    • 数字转换遵循通用规则,极大极小的数字使用指数形式
    String(null)                 // 'null'
    String(undefined)            // 'undefined'
    String(true)                 // 'true'
    String(1)                    // '1'
    String(-1)                   // '-1'
    String(0)                    // '0'
    String(-0)                   // '0'
    String(Math.pow(1000,10))    // '1e+30'
    String(Infinity)             // 'Infinity'
    String(-Infinity)            // '-Infinity'
    String({})                   // '[object Object]'
    String([1,[2,3]])            // '1,2,3'
    String(['koala',1])          //koala,1
    

    Boolean

    除了下述 6 个值转换结果为 false其他全部为 true

    • undefined
    • null
    • -0
    • 0或+0
    • NaN
    • ""(空字符串)

    假值以外的值都是真值。其中包括所有对象(包括空对象)的转换结果都是true

    甚至连false对应的布尔对象new Boolean(false)也是true

    Boolean(undefined)             // false
    Boolean(null)                  // false
    Boolean(0)                     // false
    Boolean(NaN)                   // false
    Boolean('')                    // false
    Boolean({})                    // true
    Boolean([])                    // true
    Boolean(newBoolean(false))     // true
    

    强制类型转换不同场景应用

    什么时候自动转换为string类型

    • 没有对象的前提下 字符串的自动转换,主要发生在字符串的加法运算。当一个值为字符串,另一个值为非字符串,则后者转为字符串。
    '2' + 1            // '21'
    '2' + true         // "2true"
    '2' + false        // "2false"
    '2' + undefined    // "2undefined"
    '2' + null         // "2null"
    
    • 当有对象且与对象进行加法运算
    // toString 的对象
    let obj2 = {
        toString: function() {
            return 'a'
        }
    }
    console.log('2' + obj2)     //输出结果 2a
    
    //常规对象
    let obj1 = {
        a: 1,
        b: 2
    }
    console.log('2' + obj1);   //输出结果 2[object Object]
    
    • 几种特殊对象
    '2' + {}                    // "2[object Object]"
    '2' + []                    // "2"
    '2’ + function() {}         // "2function (){}"
    '2' + ['k', 1]              // "2k,1"
    

    '2'+obj2 的详细解析步骤

    1. 左边为string,ToPrimitive 原始值转换后不发生变化
    2. 右边转化时同样按照ToPrimitive进行原始值转换,由于指定的type是number,进行ToPrimitive转化调用obj2.valueOf(),得到的不是原始值, 是对象,进行第三步
    3. 调用toString() return 'a' 是原始值
    4. 符号两边存在string,而且是+号运算符则都采用String规则转换为string类型进行拼接
    5. 输出结果2a

    '2'+obj1 的详细解析步骤

    1. 左边为string,ToPrimitive转换为原始值后不发生变化
    2. 右边转化时同样按照ToPrimitive进行原始值转换,由于指定的type是number,进行ToPrimitive转化调用obj1.valueOf(),得到 { a: 1, b: 2}
    3. 调用toString() return "[object Object]"
    4. 符号两边存在string,而且是+号运算符则都采用String规则转换为string类型进行拼接
    5. 输出结果"2[object Object]"

    注意:不管是对象还不是对象,都有一个转换为原始值的过程,也就是ToPrimitive转换,只不过原始类型转换后不发生变化,对象类型才会发生具体转换。

    什么时候自动转换为Number类型

    • 有加法运算符,但是无String类型的时候,都会优先转换为Number类型
    true + 0         // 1
    true + true      // 2
    true + false     // 1 
    
    • 除了加法运算符其他运算符都会把运算自动转成数值
    '5' - '2'        // 3
    '5' * '2'        // 10
    true - 1         // 0
    false - 1        // -1
    '1' - 1          // 0
    '5' * []         // 0
    false / '5'      // 0
    'abc' - 1        // NaN
    null + 1         // 1
    undefined + 1    // NaN
    
    //一元运算符(注意点)
    + 'abc'          // NaN
    - 'abc'          // NaN
    + true           // 1
    - false          // 0 
    
    • null转为数值时为0,而undefined转为数值时为NaN
    • 判断等号 也放在Number里面特殊说明 == 抽象相等比较与 + 运算符不同,不再是String优先,而是Number优先。
      • 如果x,y均为number,直接比较没什么可解释的了
      • 如果存在对象,ToPrimitive() type为number进行转换,再进行后面比较
    例1
    let obj1 = {
        valueOf: function() {
            return '1'
        }
    }
    1 == obj1      // true
    
    例2
    [] == ![]      // true
    
    例3
     3 == true     // false
    '0' == false   // true
    
    例4
    '0' == 0       //true
    
    • 1 == obj1 解释
    1. obj1 转为原始值,调用obj1.valueOf() 返回原始值'1'
    2. '1'toNumber 得到 1 然后比较 1 == 1 得 true
    • [] == ![] 解释
    1. []作为对象ToPrimitive得到 ''
    2. ![]作为boolean转换得到0
    3. '' == 0
    4. 转换为0==0 得到 true
    • 3 == true / '0' == false 解释
    1. 存在boolean,按照ToNumberboolean转换为1或者0,再进行后面比较
    2. 3 == 1false / 0 == 0true
    • 如果x为string,y为numberx转成number进行比较
    1. '0' toNumber()得到 0
    2. 0 == 0 true

    什么时候进行布尔转换

    • 布尔比较时
    • if(obj) , while(obj) 等判断时或者 三元运算符只能够包含布尔值
    if (!undefined && !null && !0 && !NaN && !'') {
        console.log('true');
    }
    // true
    
    • 下面两种情况也会转成布尔类型
      • expression ? true: false
      • !! expression

    数据类型判断方法

    typeof

    typeof 操作符返回一个字符串,表示未经计算的操作数的类型。

    1. null 的判定有误差,得到的结果如果使用 typeof null得到的结果是object

    2. 操作符对对象类型及其子类型,例如函数(可调用对象)、数组(有序索引对象)等进行判定,则除了函数都会得到 object 的结果。

    typeof 'nevermore'          // 'string'
    typeof true                 // 'boolean'
    typeof 10                   // 'number'
    typeof Symbol()             // 'symbol'
    typeof null                 // 'object'  无法判定是否为 null
    typeof undefined            // 'undefined'
    typeof {}                   // 'object'
    typeof []                   // 'object'
    typeof (() = >{})           // 'function'
    

    instanceof

    instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

    [] instanceof Array              // true
    ({}) instanceof Object           // true
    (() = >{}) instanceof Function   // true
    
    // instanceof 也不是万能的
    let arr = [] 
    let obj = {}
    arr instanceof Array       // true
    arr instanceof Object      // true
    obj instanceof Object      // true
    

    在这个例子中,arr 数组相当于 new Array() 出的一个实例,所以 arr.proto === Array.prototype,又因为 Array 属于 Object 子类型,即 Array.prototype.proto === Object.prototype,所以 Object 构造函数在 arr 的原型链【关联概念】上。所以 instanceof 仍然无法优雅的判断一个值到底属于数组还是普通对象。

    手动实现一下 instanceof 核心: 原型链的向上查找

    function myInstanceof(left, right) {
        //基本数据类型直接返回false
        if(typeof left !== 'object' || left === null) return false;
        
        //getProtypeOf 是 Object对象自带的一个方法,能够拿到参数的 [原型对象]
        let proto = Object.getPrototypeOf(left);
        while(true) {
          //查找到尽头,还没找到 返回false
          if(proto == null) {
            return false;
          }
          //找到相同的原型对象
          if(proto == right.prototype) {
            return true;
          }
          proto = Object.getPrototypeOf(proto);
        }
    }
    

    Object.prototype.toString() [最好]

    Object.prototype.toString.call({})              // '[object Object]'
    Object.prototype.toString.call([])              // '[object Array]'
    Object.prototype.toString.call(() => {})        // '[object Function]'
    Object.prototype.toString.call('nevermore')     // '[object String]'
    Object.prototype.toString.call(1)               // '[object Number]'
    Object.prototype.toString.call(true)            // '[object Boolean]'
    Object.prototype.toString.call(Symbol())        // '[object Symbol]'
    Object.prototype.toString.call(null)            // '[object Null]'
    Object.prototype.toString.call(undefined)       // '[object Undefined]'
    
    Object.prototype.toString.call(newDate())       // '[object Date]'
    Object.prototype.toString.call(Math)            // '[object Math]'
    Object.prototype.toString.call(newSet())        // '[object Set]'
    Object.prototype.toString.call(newWeakSet())    // '[object WeakSet]'
    Object.prototype.toString.call(newMap())        // '[object Map]'
    Object.prototype.toString.call(newWeakMap())    // '[object WeakMap]'
    

    我们可以发现该方法在传入任何类型的值都能返回对应准确的对象类型

    该方法本质就是依托Object.prototype.toString() 方法得到对象内部属性 [[Class]]

    传入原始类型却能够判定出结果是因为对值进行了包装

    nullundefined 能够输出结果是内部实现有做处理

    比较运算符

    Object.is和===的区别

    Object.is它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致 不同之处只有两个:

    • +0不等于-0,
    • NaN等于自身。
    +0 === -0           //true
    NaN === NaN         // false
    
    Object.is(+0, -0)   // false
    Object.is(NaN, NaN) // true
    

    == 和 ===有什么区别

    ===叫做严格相等,是指:左右两边不仅值要相等,类型也要相等

    例如'1'=== 1的结果是false,因为一边是string,另一边是number。

    == 不像 === 那样严格,对于一般情况,只要值相等,就返回true,但 == 还涉及一些类型转换,它的转换规则如下:

    • 两边的类型是否相同,相同的话就比较值的大小,例如 1==2,返回 false
    • 判断的是否是nullundefined,是的话就返回 true
    • 判断的类型是否是StringNumber,是的话,把String类型转换成Number,再进行比较
    • 判断其中一方是否是Boolean,是的话就把Boolean转换成Number,再进行比较
    • 如果其中一方为Object,且另一方为String、Number或者Symbol,会将Object转换成字符串,再进行比较
    console.log({a: 1} == true);                // false
    console.log({a: 1} == "[object Object]");   // true
    

    如何让if(a == 1 && a == 2)条件成立

    let a = {
      value: 0,
      valueOf: function() {
        this.value++;
        return this.value;
      }
    };
    console.log(a == 1 && a == 2); //true
    

    这是奇技淫巧,没什么用,看下理解就行。

    其他

    js 浮点计算精度丢失问题

    0.1 + 0.2 = 0.30000000000000004
    0.7 + 0.1 = 0.7999999999999999
    0.2 + 0.4 = 0.6000000000000001
    1.5 - 1.2 = 0.30000000000000004
    0.3 - 0.2 = 0.09999999999999998
    19.9 * 100 = 1989.9999999999998
    0.8 * 3 = 2.4000000000000004
    35.41 * 100 = 3540.9999999999995
    0.3 / 0.1 = 2.9999999999999996
    0.69 / 10 = 0.06899999999999999
    

    原理参考这篇 精度丢失问题-看这篇文章就够了

    解决方案 推荐用下面这个库

    github.com/nefe/number…

    ['1', '2', '3'].map(parseInt)

    主要考察 JS的映射与解析 和 parseInt这个函数的进制参数

    map接收一个 callback Function 本例就是 parseInt => 转换来看就是

    ['1', '2', '3'].map((item, index) => {
    	return parseInt(item, index)
    })
    

    那么 index 就是进制

    parseInt('1', 0) // 1
    parseInt('2', 1) // NaN
    parseInt('3', 2) // NaN, 3不是二进制
    

    输出 1, NaN, NaN

    参考

    • developer.mozilla.org/zh-CN/docs/…
    • mp.weixin.qq.com/s/Wv5K6M4lT…
    • juejin.im/post/5dac5d…

    下载网 » [概念细节] javaScript中的数据类型

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元