• 深入学习JavaScript数据类型


    数据类型是我们学习JavaScript时最先接触的东西,它是JavaScript中最基础的知识,这些知识看似简单,但实则有着许多初学者甚至是部分学习了多年JavaScript的老手所不了解的知识。

    数据类型

    ECSMAScript标准中规定了7种数据类型,这7种数据类型又分为基本型和引用类型。

    基本类型

    • Null:只包含一个值:null
    • Undefined:只包含一个值:undefined
    • Boolean:包含truefalse
    • Number:整数或浮点数,还有一些特殊值(-Infinity+InfinityNaN
    • String:字符串
    • Symbol:表示独一无二的值(ES6加入)

    ES10(ES2019)中新增了一种基本类型BigInt,可以用来表示超出number安全范围的任意精度整数。

    引用类型

    • Object对象:包含对象、数组、函数等。

    基本类型和引用类型的区别

    存放位置不同

    内存空间被分为两种:栈内存和堆内存。

    栈内存

    • 存储的值大小固定
    • 空间较小
    • 可以直接操作,效率高

    堆内存

    • 存储的值大小不确定,可以动态调整
    • 空间较大,运行效率低
    • 无法直接操作其内部,使用引用地址读取

    基本类型就属于较为简单的数据,且会被频繁使用,因此通常存放在栈中。

    var a = 10;
    var b = 'hello';
    var c = true;
    

    基本类型存储

    引用类型则是同时保存在栈和堆当中:引用类型的实际值存储在堆当中,同时它会在栈中存储一个指向堆内存中的值的地址。

    var a = 10;
    var obj1 = { name: 'nihao' };
    var obj2 = function () {
        // do something
    }
    

    引用类型存储

    基本类型具有不可变性

    var name = 'hello';
    name.toUpperCase(); // "HELLO"
    console.log(name); // "hello"
    

    由以上代码我们会发现,如果不使用name变量本身去接收toUpperCase()的返回值,那么name的值不会被改变。

    由于栈中的内存空间大小固定,所以存储在栈中的变量就是不可变的,但在使用JavaScript时我们会发现可以改变基本类型的值,例如:

    var c = true;
    c = false;
    console.log(c); // false
    

    这实际上是相当于在栈中开辟了一片新的空间用来存储false这个值,然后用变量c指向这个值,并非改变原本的true

    更改基本类型的值

    引用类型就可以很轻易的改变了,它不需要使用变量本身(obj1)去再次接收新的值就可以改变,例如:

    var obj1 = { name: 'nihao' };
    obj1.name = 'nibuhao';
    console.log(obj1); // { name: 'nibuhao' }
    

    值比较和引用比较

    对于基本类型,比较时会直接比较它们的值,相等返回true

    var str1 = 'Hello';
    var str2 = 'Hello';
    console.log(str1 === str2); // true
    

    对于引用类型,比较时会比较它们的引用地址,哪怕两个变量具有同名属性,且同名属性的值相同,但是因为存储位置不同,两者仍然不相等

    var obj1 = { name: 'obj' };
    var obj2 = { name: 'obj' };
    console.log(obj1 === obj2); // false
    

    赋值

    与上面的两种比较类似,基本类型赋值时是直接将值给另一个变量,而引用类型则是将地址给另一个变量

    var str1 = 'Hello';
    var str2 = str1;
    str2 = 'World';
    console.log(str1); // "Hello"
    //str1的值没变
    
    var obj1 = { name: 'obj1' };
    var obj2 = obj1;
    obj2.name = 'obj2';
    console.log(obj1.name); // "obj2"
    // obj1的值改变
    

    null与undefined

    • null表示空值
    • undefined表示“缺少值”,即此处应该有值,但是还未定义
    var a = null;
    console.log(typeof a); // object
    console.log(typeof b); // undefined
    

    如果学过C#Java之类的静态类型语言就会知道,直接使用未声明的变量会直接报错,而JavaScript是动态类型语言,成员除了存在空值外,还有可能根本就不存在(因为只有在运行时才会知道是否存在)。

    Symbol类型

    symbol变量需要使用Symbol()创建

    var s = Symbol(); // 注意没有new
    

    Symbol()函数接受一个可选参数,用来描述即将创建的symbol变量,无论传入的描述是否相同,最后生成的symbol一定是独一无二的

    var name1 = Symbol('Tom');
    var name2 = Symbol('Tom');
    console.log(name1 === name2); // false
    

    如果一定要创建两个一模一样的symbol,需要使用Symbol.for()

    var name1 = Symbol.for('Tom');
    var name2 = Symbol.for('Tom');
    console.log(name1 === name2); // true
    

    Symbol类型可以用作对象属性,使用该类型可以保证对象不会出现同名属性

    var obj = {
        [Symbol('name')]: 'Tom'
    };
    

    使用Symbol类型作为对象的属性名时,是无法是用for ... inObject.getOwnPropertyNamesObject.keys()获取到该属性的,可以调用用来专门获取Symbol的方法Object.getOwnPropertySymbols()来获取

    var obj = {
        [Symbol('name')]: 'Tom'
    };
    for (var key in obj) {
       console.log(key); // undefined
    }
    Object.getOwnPropertySymbols(obj); // [Symbol(name)]
    

    数据类型转换

    图片来源:https://juejin.im/post/5cec1bcff265da1b8f1aa08f#heading-23

    宽松等号(==)的隐式转换

    使用==时,如果等号两侧的数据类型相同,那么比较结果与===相同,否则会发生隐式转换

    NaN

    NaN和任何类型比较都会返回false,包括他自己

    NaN == NaN // false
    

    Boolean类型与其他类型进行比较

    只要Boolean类型参与比较,该Boolean类型的值都会被转换为Number类型,1转为true0转为false

    false == 0 // true
    true == 1 // true
    true == 2 // false
    

    如果在使用if判断时,我们使用数字作为判断条件

    var x = 10;
    if (x) {
    	// ...
    }
    if (x == true) {
        // ...
    }
    

    我们会发现,第一个判断结果为true,而第二个的结果为false,这就是因为true被转换为了1,判断条件变为了x == 1

    Number类型和String类型进行比较

    这两者进行比较时,String类型会被转为Number类型,除了纯数字字符串正常转换为Number类型外,空字符串''转为0,科学计数法(例如1e11)正常转换,Infinity正常转换,其他全部转换为NaN

    '' == 0 // true
    '123' == 123 // true
    '1e11' == 1e11 // true
    Infinity == 'Infinity' // true
    true == '1' // true
    false == '0' // true
    

    null与undefined

    null == undefined结果为true外,其他任何类型和nullundefined比较都为false

    基本类型与引用类型比较

    ToPrimitive规则

    首先我们要先了解ToPrimitive规则,即引用类型转为基本类型

    • 当引用类型需要被转为基本类型时,它会先查找对象的valueOf方法,如果该方法返回基本类型的值,则ToPrimitive的结果就是这个值
    • 如果valueOf不存在或者valueOf方法返回的不是基本类型,就会尝试调用对象的toString方法,然后使用toString的返回值作为ToPrimitive的结果
    • valueOftoString都不存在,或者没有返回基本类型,则抛出TypeError异常

    对于不同的引用类型,该过程会有些许不同,比如Date会先调用toString

    引用类型转换为不同的基本类型也会有一些不同,比如:

    • 引用类型转换为Number类型,先调用valueOf,再调用toString
    • 引用类型转换为String类型,先调用toString,再调用valueOf

    具体请参考ECMA标准

    Number([]); // 0
    Number([10]); // 10
    
    var obj = {
        valueOf: () => { return 10; },
        toString: () => { return -10; }
    };
    Number(obj); // 10
    String(obj); // -10
    

    基本类型与引用类型比较

    比较时,引用类型会依据ToPrimitive规则转换为基本类型

    var a = {};
    var b = [1, 2, 3];
    
    a == '[object Object]'; // true
    a.toString(); // [object Object]
    b == '1,2,3' // true
    b.toString(); // "1,2,3"
    

    判断数据类型

    typeof

    typeof只能用来判断以下几个类型

    typeof 'str';  // string
    typeof 123;  // number
    typeof true;  // boolean
    typeof Symbol();  // symbol
    typeof undefined;  // undefined
    typeof function () {} // function
    

    对于引用类型(数组、对象等)以及nulltypeof的返回值均为object

    instanceof

    instanceof可以判断引用类型的具体类型

    [] instanceof Array; // true
    /1/ instanceof RegExp; // true
    new Date() instanceof Date; // true
    

    但是,instanceof同样没法判断null

    null instanceof null; // Uncaught TypeError: Right-hand side of 'instanceof' is not an object
    null instanceof Object; // false
    

    MDN中,instanceof被这样描述:

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

    也就是说instanceof设计的初衷并不是用来检测数据类型的,因此很多类型它也无法判断

    Object.prototype.toString.call()

    Object.prototype.toString.call()利用call来改变this的指向,可以让该方法能够获取到任意变量的[[class]]属性,通过该属性可以判断所有JavaScript的内置类型

    Object.prototype.toString.call(null); // [object Null]
    Object.prototype.toString.call(undefined); // [object Undefined]
    Object.prototype.toString.call(123); // [object Number]
    Object.prototype.toString.call(new Date()); // [object Date]
    // ...
    

    但是该方法并不能判断自定义类型,而instanceof可以实现对自定义类型的判断

    function Person() {}
    Object.prototype.toString.call(new Person()); // [object Object]
    new Person() instanceof Person; // true
    

    参考资料

  • 相关阅读:
    03JavaScript程序设计修炼之道 2019-06-23_15-50-11 验证码
    03JavaScript程序设计修炼之道 2019-06-23_15-21-11 全选和反选
    python进阶(10)迭代器
    python进阶(9)多线程
    python进阶(8)多进程
    python进阶(7)垃圾回收机制
    python进阶(6)深拷贝和浅拷贝
    python进阶(5)异常模块
    python进阶(4)文件操作
    python进阶(3)json文件与python字典的转化
  • 原文地址:https://www.cnblogs.com/xueyubao/p/13280474.html
Copyright © 2020-2023  润新知