美文网首页JavaScript基础学习
JavaScript ES6数据类型

JavaScript ES6数据类型

作者: Mstian | 来源:发表于2020-01-08 10:18 被阅读0次

    JavaScript 是一种弱类型或者说动态语言。这意味着你不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。这也意味着你可以使用同一个变量保存不同类型的数据:

    //例如可以声明一个变量:
    var a = 1;
    a = 'leilei'
    a = true
    

    这种形式在JavaScript语法中不会报错,但是会有潜在的一些问题,因此微软推出的JavaScript的超集TypeScript就解决了这个问题;在声明一个变量之前首先声明他的类型;(示例为ts代码)

    //声明变量的类型及初始值:
    var [变量名] : [类型] = 值;
    var uname:string = "leilei";
    

    数据类型
    ECMAScript标准定义了8中数据类型:

    • 7种原始类型
      • Boolean
      • Null
      • Undefined
      • Number
      • BigInt
      • String
      • Symbol
    • Object

    原始值( primitive values )
    除 Object 以外的所有类型都是不可变的(值本身无法被改变)。例如,与 C 语言不同,JavaScript 中字符串是不可变的(译注:如,JavaScript 中对字符串的操作一定返回了一个新字符串,原始字符串并没有被改变)。我们称这些类型的值为“原始值”。

    布尔类型
    布尔表示一个逻辑实体,可以有两个值:true 和 false。

    Null 类型
    Null 类型只有一个值:null。

    Undefined 类型
    一个没有被赋值的变量会有个默认值 undefined。

    Null 和 Undefined 区别(面试问到过)

    console.log(null==undefined)//true
    console.log(null===undefined)//false
    

    null: object类型,代表“空值”,代表一个空对象指针,
    undefined: undefined类型,

    null和 undefined都表示“值的空缺”,可以认为undefined是表示系统级的、出乎意料的或类似错误的值的空缺,

    null是表示程序级的、正常的或在意料之中的值的空缺。

    undefined是访问一个未初始化的变量时返回的值,而null是访问一个尚未存在的对象时所返回的值。

    因此,可以把undefined看作是空的变量,而null看作是空的对象。

    场景:
    null
    (1) 作为函数的参数,表示该函数的参数不是对象。
    (2) 作为对象原型链的终点。
    undefined
    (1)变量被声明了,但没有赋值时,就等于undefined。
    (2)调用函数时,应该提供的参数没有提供,该参数等于undefined。
    (3)对象没有赋值的属性,该属性的值为undefined。
    (4)函数没有返回值时或者return后面什么也没有,返回undefined。

    数字类型
    JavaScript 只有一种数字类型。它并没有为整数给出一种特定的类型,只要在双精度浮点数的取值范围内在JavaScript中数字就是安全的。超出范围那么JavaScript 数字就不再安全了。具体检测数值是否安全可以参考 MDN
    还有一些带符号的值:+Infinity,-Infinity 和 NaN (非数值,Not-a-Number)。

    BigInt 类型
    BigInt类型是 JavaScript 中的一个基础的数值类型,可以用任意精度表示整数。使用 BigInt,您可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。BigInt是通过在整数末尾附加 n 或调用构造函数来创建的。

    字符串类型
    JavaScript 字符串是不可更改的。这意味着字符串一旦被创建,就不能被修改。

    符号类型
    符号(Symbols)是ECMAScript 第6版新定义的。符号类型是唯一的并且是不可修改的, 并且也可以用来作为Object的key的值(如下). 在某些语言当中也有类似的原子类型(Atoms). 你也可以认为为它们是C里面的枚举类型

    symbol类型是一种基本数据类型 ([primitive data type]。
    Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。
    它的静态属性会暴露几个内建的成员对象;
    它的静态方法会暴露全局的symbol注册,且类似于内建对象类,
    但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。
    每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。

    Symbol类型详解
    创建symbol实例

    //可以通过调用Symbol()函数来创建一个Symbol实例:
    let symbol1 = Symbol();
    
    //可以在调用Symbol()函数时传入一个可选的字符串参数,相当与给该实例一个描述信息
    let symbol2 = Symbol('this is symbol2');
    

    检查类型(使用typeof)

    typeof symbol1  // 'symbol'
    

    对比示例

    let symbol1 = Symbol();
    let symbol2 = Symbol('this is symbol2');
    let symbol3 = Symbol();
    symbol1  ==  symbol3 //false
    symbol1  ===  symbol3 //false
    symbol1  == symbol2 //false
    

    使用场景1:常量

    //常量
    const type1 = 'type1';
    const type2 = 'type2';
    const type3 = 'type3';
    
    function testType(type){
        switch(type){
             case type1:
               console.log(type)
               break;
            case type2:
               console.log(type)
               break;
            case type3:
               console.log(type)
               break;
            default:
               throw new Error('type error')
        }
    }
    
    //可以使用
    const type1 = Symbol();
    const type2 = Symbol();
    const type3 = Symbol();
    
    //来替换以上常量声明,避免命名困难强迫症陷阱,并且完美保证每个常量的值都是唯一。
    

    使用场景2:对象属性名

    //我们知道对象的key是保持唯一的,因此有时候可以用对象key的特性来进行数组去重。symbol的特性可以用来做对象的key值。
    //特殊的一点是在使用Symbol 类型作为对象的key值时需要使用中括号 [ ] 来讲Symbol类型括起来。 
    let obj = {
      name : 'ray',
      age:23  
    }
    obj.name // 'ray'
    obj[age] // 23
    
    const name = Symbol();
    const age = Symbol();
    
    let obj = {
       [name]:'ray'
    }
    obj[age] = 23;
    
    obj[name] //'ray';
    obj[age] // 23
    
    //此时我们输出obj
    console.log(obj); //{Symbol(): "ray", Symbol(): 23}
    //很显然对象key值为Symbol();
    //如果我们使用for in 循环去遍历obj的key会得出什么呢?
    for(let key in obj){
      console.log(key); // 
    }
    //此时的输出竟然是空的 ......
    //对了,输出确实是空的。因为Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,
    //它未被包含在对象自身的属性名集合(property names)之中。
    //接着看
    var symbol = Symbol();
    var obj1 = {
      name:'ray',
      age:24,
      [symbol] : 'symbol'
    }
    Object.keys(obj1) // ['name','age'];
    //那它的[symbol] key值去哪了,怎么访问到呢???
    obj1[symbol]   // 'symbol'
    //放心 可以访问到我们定义好的值,只是不能通过常规的手段去获取而已。
    //例如,使用 JSON.stringify() 转换成字符串时 Symbol属性也会被排除;
    JSON.stringify(obj1) // "{"name":"ray","age":24}",此时也是没有Symbol属性的。
    
    //但是有专门的API就是针对Symbol的 例如:
    // 使用Object的API
    Object.getOwnPropertySymbols(obj1) // [Symbol()]
    
    // 使用新增的反射API
    Reflect.ownKeys(obj1) // ['name', 'age',Symbol(symbol)]
    
    

    总结:在使用Symbol此类型的数据作为对象的key时,无法通过Object.keys()和for in 循环去处理获取对象的Symbol类型key值,使用JSON.stringify()去转换对象为字符串时,也会隐藏掉以Symbol为key的属性名和属性值。但是可以使用Object.getOwnPropertySymbols(obj)与Reflect.ownKeys(obj)来获取以Symbol类型作为对象的key的键的,实际上这也为对象创建了其私有属性。

    关于Symbol的使用还有很多,本文Symbol内容部分知识参考大神
    一斤代码《理解和使用ES6中的Symbol》


    JavaScript 重量级人物来了,他就是对象

    对象(Object)

    相信对象对每个程序员来说都不陌生,毕竟办公室里除了敲代码就是谈论怎么找对象,然后年级大点的老程序员(其实他还不到30岁)就说“哎呀 对象嘛,哪里有那么难搞,New一个出来就完了嘛”。

    咳咳,言归正传,相信每个JavaScript程序员都听说过,在JavaScript中,万物皆对象。我认为对于这句话理解的透彻程度,决定了一个JavaScript程序员的自我修养深度。

    先来点MDN的概念,虽然有的看不太懂,但是就是觉得人家说得好,哈哈。

    在计算机科学中, 对象是指内存中的可以被标识符引用的一块区域.

    在 Javascript 里,对象可以被看作是一组属性的集合。用对象字面量语法来定义一个对象时,会自动初始化一组属性。(也就是说,你定义一个var a = "Hello",那么a本身就会有a.substring这个方法,以及a.length这个属性,以及其它;如果你定义了一个对象,var a = {},那么a就会自动有a.hasOwnProperty及a.constructor等属性和方法。)而后,这些属性还可以被增减。属性的值可以是任意类型,包括具有复杂数据结构的对象。属性使用键来标识,它的键值可以是一个字符串或者符号值(Symbol)。

    一个 Javascript 对象就是键和值之间的映射.。键是一个字符串(或者 Symbol)Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。 值可以是任意类型的值。 这使得对象非常符合哈希表

    函数是一个附带可被调用功能的常规对象。

    数组是一种使用整数作为键(integer-key-ed)属性和长度(length)属性之间关联的常规对象。此外,数组对象还继承了 Array.prototype 的一些操作数组的便捷方法。例如, indexOf (搜索数组中的一个值) or push (向数组中添加一个元素),等等。 这使得数组是表示列表或集合的最优选择。

    JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,来源于 JavaScript 同时也被多种语言所使用。 JSON 用于构建通用的数据结构。

    总之,这段话说明了一件事,那就是数字,字符串,函数,数组,JSON等都是对象。

    万物皆对象就完事了。

    创建对象
    1.字面量:

    var obj = {
      name:'leilei',
      age:23
    }
    

    2.使用new操作符:

    var obj = new Object();
    obj.name = 'leilei';
    obj.age = 23;
    

    3.Object.create():

    var obj = {a:1}
    var obj1 = Object.create(obj)
    console.log(obj1) // 输出{} ????? 下面有原因
    console.log(obj1.a) // 输出1
    

    4.构造函数模式

    function Person(name,age){
      this.name = name;
      this.age = age;
    }
    var person1 = new Person('leilei',23);
    console.log(person1) // Person {name: "leilei", age: 23}
    

    5.工厂模式

    function person(name,age){
      var o = new Object();
      o.name = name;
      o.age = age;
      return o;
    }
    var person1 = person('leilei',23);
    console.log(person1); //{name: "leilei", age: 23}
    

    6.原型模式

    function Person(){}
    Person.prototype.name = 'leilei';
    Person.prototype.age = 23;
    var person1 = new Person();
    console.log(person1); //Person {}
    console.log(person1.name); //'leilei'
    person1.sex = 'male';
    console.log(person1);// Person {sex: "male"}
    person1.name = 'zhaohui';
    console.log(person1);// Person {sex: "male", name: "zhaohui"}
    

    7.混合模式(构造函数模式+原型模式)

    function Person(name,age){
      this.name = name;
      this.age = age;
    }
    Person.prototype.sex = 'male';
    var person1 = new Person('leilei',23);
    console.log(person1); // Person {name: "leilei", age: 23}
    console.log(person1.sex)  // 'male'
    

    以上是对象的几种创建方法,里面有很多细节都没有详细的说明,主要是因为本文的重点不在这块,而且在平时工作中我自己个人使用的较少,也是害怕写错了误人,以上写过的代码都是测试过没有问题的。
    以下是对于上面的一些补充,以及面试经历过关于这块的问题:
    1.对象创建的几种方式
    见上几种;
    2.对象创建new 和Object.creact()的区别
    首先,看看MDN上关于Object.create()方法的说明:

    Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。 (不理解的话继续往下看)
    语法:
    Object.create(proto[, propertiesObject])
    参数:
    proto:新创建对象的原型对象。
    propertiesObject:可选。如果没有指定为 undefined,则是要添加到新创建对象的不可枚举(默认)属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。
    返回值:
    一个新对象,带着指定的原型对象和属性。

    看代码:

    var obj = {a:1}
    var o = Object.create(obj);
    console.log(o);  // {}  空对象
    console.log(o.a);  //  1
    console.log(o.__proto__)  // {a: 1}
    
    /*由定义可知,Object.create()使用现有的对象来提供新创建的对象的proto,所以可以看出属性a不是对象o自身的属性,
    而是通过其原型链访问到的对象obj的属性,相当于将对象obj放到了对象o的原型上,也可以说o继承了obj,将obj的属性
    添加到了其原型下。*/
    //因此Object.keys(o) // [];
    //使用for in 循环也获取不到key值。
    

    然后再看看他的第二个参数 propertiesObject

    Object.create() 用第二个参数来创建非空对象的属性描述符默认是为false的;

    什么意思呢?先看代码:

    var obj = {a:1}
    var o = Object.create(obj,{des:{value:123}})
    console.log(o) // {des: 123}
    Object.keys(o) // [];
    o.des = 456;
    console.log(o.des); //123 ,没有变为456
    //现在使用Object.getOwnPropertyDescriptors()来看一下
    Object.getOwnPropertyDescriptors(o);
    输出为:
    {
      des:{
        configurable:false, //不可配置
        enumerable:false, //不可枚举
        value:123,
        writable:false //不可写
      }
    }
    

    以上可以看出,为什么使用Object.keys(),和for in 循环不能获取Object.create()的第二个参数,以及为什么修改des为456之后再次去获取还是123;
    那么如何让它可以枚举,可以写入,可以配置呢?

    o2 = Object.create({}, {
      p: {
        value: 2019, 
        writable: true, //可写
        enumerable: true, //可枚举
        configurable: true  //可配置
      } 
    });
    o2.p = 2020;
    console.log(o2) // {p:2020};
    

    总结:Object.create()方法可以创建对象,创建一个新对象,使用现有的对象来提供新创建的对象的proto;(现在理解这句话有点清晰了吧)
    由于是在原型上,所以使用Object.keys(),for in 循环获取key值自然是拿不到的。
    Object.create()的第二个参数创建非空对象的属性描述符默认是为false的;也就是不可写,不可配置,不可枚举,但是可以通过自己修改来使其可以配置,可以写入,可以枚举。
    使用Object.getOwnPropertyDescriptors();方法可以获取Object.create()第二个参数具体属性。

    这样大概也就对Object.create()有了一个大概了解,接着再看new Object()与Object.create()的区别:
    1.创建对象的方式不同
    new Object() 通过构造函数来创建对象, 添加的属性是在自身实例下。
    Object.create() es6创建对象的另一种方式,可以理解为继承一个对象, 添加的属性是在原型下。
    2.创建对象属性的性质不同
    Object.create() 用第二个参数来创建非空对象的属性描述符默认是为false的,
    而构造函数或字面量方法创建的对象属性的描述符默认为true。
    3.创建空对象时不同
    当用构造函数或对象字面量方法创建空对象时,对象时有原型属性的,即有proto;
    当用Object.create()方法创建空对象时,对象是没有原型属性的。

    new 与 Object.create() 创建空对象

    本文Object.create()内容部分知识参考大神liwuwuzhi的文章《Object.create()》

    Set

    Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

    const set2 = new Set([{a:1}, '2', 3, [4,5], 6,false]);
    
    set2

    语法:
    new Set([iterable]);
    参数:
    iterable
    如果传递一个可迭代对象,它的所有元素将不重复地被添加到新的 Set中。如果不指定此参数或其值为null,则新的 Set为空。
    返回值:
    一个新的Set对象。
    简述:
    Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

    属性:
    Set.prototype.size:返回Set对象的值的个数。

    var set = new Set([1,2,3,4,5]);
    set.size // 5
    

    方法:
    Set.prototype.add(value);在Set对象尾部添加一个元素。返回该Set对象。

    var set = new Set([1,2,3,4,5]);
    set.add(6);
    set // Set [1,2,3,4,5,6]; 
    

    Set.prototype.clear();移除Set对象内的所有元素。

    var set = new Set([1,2,3,4,5]);
    set.clear();
    set // Set []
    

    Set.prototype.delete(value);移除Set的中与这个值相等的元素,返回Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在,返回true,否则返回false)。Set.prototype.has(value)在此后会返回false。

    var set = new Set([1,2,3,4,5]);
    set.delete(1); //true
    set // Set [2,3,4,5]
    

    Set.prototype.has(value);返回一个布尔值,表示该值在Set中存在与否。

    var set = new Set([1,2,3,4,5]);
    set.has(1) // true;
    set.has('abc') // false
    

    Set.prototype.values();返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值.

    var set = new Set([1,2,3,4,5]);
    set.values() // SetIterator[1,2,3,4,5]
    

    Set.prototype.keys();与values()方法相同,返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。

    var set = new Set([1,2,3,4,5]);
    set.keys() // SetIterator[1,2,3,4,5]
    

    操作:

    //交集:
    let intersection = new Set([...set1].filter(x => set2.has(x)));
    //差集:
    let difference = new Set([...set1].filter(x => !set2.has(x)));
    

    与数组在相互转换

    //Set 转 Array
    var set = new Set([1,2,3])
    var arr = Array.from(set);
    arr // [1,2,3]
    //或者 使用扩展运算符
    var arr = [...set]
    
    //Array转 Set
    var arr = [1,2,3]
    var set = new Set(arr);
    set // Set [1,2,3]
    

    数组去重

    var arr = [1,2,2,4,3,4,5,6,7,5];
    var uniqArr = [...new Set(arr)]; 
    uniqArr // [1,2,3,4,5,6,7]
    //或者使用Array.from()
    var uniqArr = Array.from(new Set(arr))
    

    更多Set对象资料请参考MDN......

    Map

    Map对象保存键值对,并且能够记住的原始插入顺序。任何值(对象或者原始值)都可以作为一个键或一个值。

    Map数据结构

    语法:
    new Map([iterable])
    参数:
    Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, 'one' ],[ 2, 'two' ]])。 每个键值对都会添加到新的 Map。null 会被当做 undefined。
    描述:
    一个Map对象在迭代时会根据对象中元素的插入顺序来进行 — 一个 for...of循环在每次迭代后会返回一个形式为[key,value]的数组。

    Objects和maps

    • 一个Object的键只能是字符串或者Symbols,但一个Map的键可以是任意值,包括函数,对象,基本类型。
    • Map中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map对象是按插入的顺序返回键值。
    • 你可以通过size属性直接获取一个Map的键值对个数,而Object的键值对个数只能手动计算。
    • Map可直接进行迭代,而Object的迭代需要先获取它的键数组,然后再进行迭代。
    • Object都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
    • Map 在涉及频繁增删键值对的场景下会有些性能优势。

    属性:
    Map.length:属性的length的值为0;
    想要计算一个Map中的条目数量,使用Map.prototype.size.
    Map 实例
    所有的Map对象实例都会继承Map.prototype。
    实例属性:
    Map.prototype.size:返回Map对象的键/值对的数量。

    var map = new Map([[1,2],[3,4]])
    map.size // 2
    

    方法:
    Map.prototype.clear();移除Map对象的所有键/值对。

    var map = new Map([[1,2],[3,4]])
    map.clear();
    

    Map.prototype.delete(key);如果Map对象中存在该元素,则移除它并返回true,否则如果钙元素不存在则返回false,

    var map = new Map([[1,1],[2,2]])
    map.delete(1); // true
    map // Map(1){2=>2}
    

    Map.prototype.entries() 返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的[key,value]数组。

    var map = new Map([[1,1],[2,2]]);
    var map2 = map.entries();
    map2 // MapIterator{1=>1, 2=>2}
    

    Map.prototype.get(key)返回键对应的值,如果不存在,则返回undefined;

    var map = new Map([[1,1],[2,2]]);
    map.get(1) // 1
    

    Map.prototype.has(key);返回一个布尔值,表示Map实例是否包含键对应的值。

    var map = new Map([[1,1],[2,2]]);
    map.has(1) // true
    map.has(4) // false
    

    Map.prototype.keys() 返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的键。

    var map = new Map([[1,1],[2,2]]);
    map.keys() // MapIterator {1, 2 } 
    

    Map.prototype.values();返回一个新得Iterator对象,它按插入顺序包含了Map对象中每个元素的值。

    var map = new Map([['键1','值1'],['键2','值2']]);
    map.values() // MapIterator {"值1", "值2"}
    

    Map.prototype.set(key,value) ;设置Map对象中键的值。返回该Map对象。

    let myMap = new Map();
    let keyObj = {};
    let ketFunc = function(){};
    let keyString = 'string';
    
    myMap.set(keyObj ,'Obj值');
    myMap.set(ketFunc ,'Func值');
    myMap.set(keyString,'Str值');
    
    myMap.size // 3
    myMap // Map(3) {{…} => "Obj值", ƒ => "Func值", "string" => "Str值"}
    

    Map与数组的关系

    var kvArray = [['key1','value1'],['key2','value2']]
    //数组转换为Map对象
    let myMap = new Map(kvArray);
    myMap.get('key1'); // 'value1'
    
    //Map对象转换为数组
    var myArr = Array.from(myMap);
    myArr // [['key1','value1'],['key2','value2']]
    //或者使用展开运算符
    var myArr = [...myMap]
    
    //也可以得到键,或者值的数组
    console.log(Array.from(myMap.keys())); // 输出 ["key1", "key2"]
    

    复制或合并Maps

    //复制
    let original = new Map([
      [1, 'one']
    ]);
    let clone = new Map(original);
    console.log(clone.get(1)); // one
    console.log(original === clone); // false. 浅比较 不为同一个对象的引用
    //合并 会保持键的唯一性
    let first = new Map([
      [1, 'one'],
      [2, 'two'],
      [3, 'three'],
    ]);
    let second = new Map([
      [1, 'uno'],
      [2, 'dos']
    ]);
    // 合并两个Map对象时,如果有重复的键值,则后面的会覆盖前面的。
    // 展开运算符本质上是将Map对象转换成数组。
    let merged = new Map([...first, ...second]);
    console.log(merged.get(1)); // uno
    console.log(merged.get(2)); // dos
    console.log(merged.get(3)); // three
    

    更多Map对象资料请参考MDN

    相关文章

      网友评论

        本文标题:JavaScript ES6数据类型

        本文链接:https://www.haomeiwen.com/subject/ynbjactx.html