• 技术文章 >web前端 >前端问答

    es6新增的js基本数据类型有哪些

    青灯夜游青灯夜游2022-10-17 17:53:42原创292

    es6新增的基本数据类型:1、Symbol类型,表示独一无二的值,即Symbol实例是唯一、不可变的;它的产生是因为要用来唯一的标记,进而用作非字符串形式的对象属性,是确保对象属性使用唯一标识符,不会发生属性冲突的危险。2、BigInt类型,提供对任意长度整数的支持,主要是为了表达大于“2^53-1”的整数。

    大前端成长进阶课程:进入学习

    本教程操作环境:windows7系统、ECMAScript 6版、Dell G3电脑。

    基本数据类型 也称为原始数据类型,包括String、Number、Boolean、undefined、null、Symbol、BigInt,其中SymbolBigInt为ES6新增。

    Symbol 类型

    Symbol 是 ECMAScript6 中引入的一种新的数据类型,表示独一无二的值。Symbol 是原始值(基础数据类型),且 Symbol 实例是唯一、不可变的。它的产生是因为要用来唯一的标记,进而用作非字符串形式的对象属性,是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

    在 ES6 之前,对象的键只能是字符串类型,但是这样有个问题,就是会造成键名命名冲突,后者覆盖前者,这个时候就需要一个唯一值来充当键名,Symbol 横空出世。

    1、概念

    symbol 是一种基本数据类型,Symbol()函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。但是它不是构造函数,不能用 new Symbol()来创建。

    let symbol = Symbol();
    typeof symbol; // "symbol"

    Symbol 作为对象属性时,当在对象内部时,必须要用方括号括起来,不用方括号括起来代表的是字符串。

    let s = Symbol();
    let obj = {
      [s]: "Jack",
    };
    obj[s]; // "Jack"
    obj.s; // undefined

    而且当要取该属性的值时,不能用点运算符,因为点运算符后面同样是字符串类型。

    创建 Symbol 数据类型时,都是 Symbol()这么创建的,当打印出来时,都为 Symbol(),这样很难区别各个 Symbol 类型的变量是什么意思。所以在 Symbol 函数内可以接收一个字符串的参数,表示该定义 Symbol 类型变量的描述。

    let s1 = Symbol("a");
    console.log(s1); // Symbol(a)
    s1.toString(); // "Symbol(a)"

    如果 Symbol 类型接收的一个对象类型的话,那就会先调用其内部的 toString 方法,将其变为一个字符串,然后才生成一个 Symbol 值。

    let arr = [1, 2, 3];
    let s1 = Symbol(arr);
    console.log(s1); // Symbol(1,2,3)
    let obj = {
      toString: () => "abc",
    };
    let s2 = Symbol(obj);
    console.log(s2); // Symbol(abc)

    Symbol 类型的变量是不能和其他变量参与运算的,而且其只能转为 String 类型和 Boolean 类型。

    let s = Symbol();
    console.log("1" + s); // TypeError: Cannot convert a Symbol value to a string
    s.toString(); // "Symbol()"
    Boolean(s); // true
    Number(s); // TypeError: Cannot convert a Symbol value to a number

    2、Symbol.prototype.description

    当给 Symbol 添加描述时,可以通过 Symbol.prototype.description 来获取该描述。

    let s = Symbol("Jack");
    s.description; // 'Jack'

    3、Symbol.for(key)和 Symbol.keyFor(sym)

    最开始看到这两个方法时,我以为是两个遍历的方法。

    let s1 = Symbol.for("foo");
    let s2 = Symbol.for("foo");
    s1 === s2; // true

    Symbol.for 会搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。所以由其创建的两个相同描述的值会相等。这种创建就和普通的 Symbol()有着截然不同的结果了:

    let s1 = Symbol("foo");
    let s2 = Symbol("foo");
    s1 === s2; // false

    因为不管怎样 Symbol()返回的都是一个全新的值,换句话说 Symbol()生成的值没有注册在全局中,所以返回的值都是全新的,而 Symbol.for()会在先在全局中查找,有就返回这个值,没有则创建新的值,但新的值也是挂载在全局中的。

    Symbol.keyFor(sym)是在全局中查找是否有该 Symbol 值,有则返回该描述。

    let s1 = Symbol.for("Jack");
    Symbol.keyFor(s1); // 'Jack'
    let s2 = Symbol("Rose");
    Symbol.keyFor(s2); // undefined

    因为 s2 没有挂载在全局中,所以 Symbol.keyFor()找不到它,故返回 undefined。

    4、内置的 Symbol 属性

    除了定义自己使用的 Symbol 值以外,ES6 还提供了 13(有可能今后会更多 ?) 个内置的 Symbol 值,指向语言内部使用的方法。

    4.1 Symbol.asyncIterator

    Symbol.asyncIterator 符号指定了一个对象的默认异步迭代器。如果一个对象设置了这个属性,它就是异步可迭代对象,可用于 for await...of 循环。换句话说一个异步可迭代对象内部必须有 Symbol.asyncIterator 属性。

    const myAsyncIterable = new Object();
    myAsyncIterable[Symbol.asyncIterator] = async function* () {
      yield "hello";
      yield "async";
      yield "iteration!";
    };
    
    (async () => {
      for await (const x of myAsyncIterable) {
        console.log(x);
        // expected output:
        //    "hello"
        //    "async"
        //    "iteration!"
      }
    })();

    当执行 for await...of 时,就会执行该变量中 Symbol.asyncIterator 属性值。

    4.2、Symbol.hasInstance

    Symbol.hasInstance 用于判断某对象是否为某构造器的实例。因此你可以用它自定义 instanceof 操作符在某个类上的行为。换句话说当判断一个实例是否为一个类的实例时,其实就是执行该类里面的 Symbol.hasInstance 属性。

    class Fu {
      [Symbol.hasInstance](num) {
        return num === 1;
      }
    }
    1 instanceof new Fu(); // true
    2 instanceof new Fu(); // false

    4.3、Symbol.isConcatSpreadable

    内置的 Symbol.isConcatSpreadable 符号用于配置某对象作为 Array.prototype.concat()方法的参数时是否展开其数组元素。

    // 默认情况下
    let arr = [1, 2, 3];
    let brr = [4, 5, 6];
    arr.concat(brr); // [1, 2, 3, 4, 5, 6]
    // 设置了Symbol.isConcatSpreadable后
    let arr = [1, 2, 3];
    let brr = [4, 5, 6];
    brr[Symbol.isConcatSpreadable] = false;
    arr.concat(brr); // [1, 2, 3, [4, 5, 6]]

    将数组的 Symbol.isConcatSpreadable 属性设置为 false 后,使用 concat 方法时该数据就不会展开。

    对于类数组而言,默认数组使用 concat 方法该类数组是不展开的,我们可以给类数组的 Symbol.isConcatSpreadable 设置为 true,这样就可以展开了,并且完成了类数组转换为数组,这样类数组转数组又多了一个方法。

    // 默认情况下
    function foo(x, y) {
      let arr = [].concat(arguments);
      console.log(arr); //[Arguments(2)]
    }
    foo(1, 2);
    // 设置了Symbol.isConcatSpreadable为true后
    function foo(x, y) {
      arguments[Symbol.isConcatSpreadable] = true;
      let arr = [].concat(arguments);
      console.log(arr); //[1, 2]
    }
    foo(1, 2);

    4.4、Symbol.iterator

    Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被 for...of 循环使用。

    const myIterable = {};
    myIterable[Symbol.iterator] = function* () {
      yield 1;
      yield 2;
      yield 3;
    };
    
    [...myIterable]; // [1, 2, 3]

    对象进行 for...of 循环时,会调用 Symbol.iterator 方法,

    4.5、Symbol.match

    Symbol.match 指定了匹配的是正则表达式而不是字符串。String.prototype.match() 方法会调用此函数。换句话说就是当 str.match()执行时如果该属性存在,就会返回该方法的返回值。

    class foo {
      [Symbol.match](string) {
        return string;
      }
    }
    "Jack".match(new foo()); // 'Jack'

    除上述之外,MDN 还提出了该属性另外一个功能:此函数还用于标识对象是否具有正则表达式的行为。比如, String.prototype.startsWith(),String.prototype.endsWith() 和 String.prototype.includes() 这些方法会检查其第一个参数是否是正则表达式,是正则表达式就抛出一个 TypeError。现在,如果 match symbol 设置为 false(或者一个 假值),就表示该对象不打算用作正则表达式对象。

    "/bar/".startsWith(/bar/); // TypeError: First argument to String.prototype.startsWith must not be a regular expression
    // 当设置为false之后
    var re = /foo/;
    re[Symbol.match] = false;
    "/foo/".startsWith(re); // true
    "/baz/".endsWith(re); // false

    4.6、Symbol.matchAll

    Symbol.matchAll 返回一个迭代器,该迭代器根据字符串生成正则表达式的匹配项。此函数可以被 String.prototype.matchAll() 方法调用。

    "abc".matchAll(/a/);
    // 等价于
    /a/[Symbol.matchAll]("abc");

    4.7、Symbol.replace

    Symbol.replace 这个属性指定了当一个字符串替换所匹配字符串时所调用的方法。String.prototype.replace() 方法会调用此方法。

    String.prototype.replace(searchValue, replaceValue);
    // 等同于
    searchValue[Symbol.replace](this, replaceValue);
    // 例子
    class Replace1 {
      constructor(value) {
        this.value = value;
      }
      [Symbol.replace](string) {
        return `s/${string}/${this.value}/g`;
      }
    }
    
    console.log("foo".replace(new Replace1("bar"))); // "s/foo/bar/g"

    4.8、Symbol.search

    Symbol.search 指定了一个搜索方法,这个方法接受用户输入的正则表达式,返回该正则表达式在字符串中匹配到的下标,这个方法由以下的方法来调用 String.prototype.search()。

    String.prototype.search(regexp);
    // 等价于
    regexp[Symbol.search](this);
    // 例子
    class Search1 {
      [Symbol.search](str) {
        return `${str} Word`;
      }
    }
    "Hello".search(new Search1()); // Hello Word

    4.9、Symbol.species

    Symbol.species 是个函数值属性,其被构造函数用以创建派生对象,换句话说 species 访问器属性允许子类覆盖对象的默认构造函数。

    我们举个例子:

    // 默认情况下
    class MyArray extends Array {}
    let arr = new MyArray(1, 2, 3);
    let brr = arr.map((item) => item);
    brr instanceof MyArray; // true
    brr instanceof Array; // true

    类 MyArray 继承于 Array,arr 为 MyArray 的实例,brr 为 arr 的衍生物,所以 brr 是 MyArray 的实例,并且由于原型链的缘故,brr 也是 Array 的实例。如果此时,我们只想让 brr 为 Array 的实例,那 Symbol.species 属性值就派上用场了。

    class MyArray extends Array {
      static get [Symbol.species]() {
        return Array;
      }
    }
    let arr = new MyArray(1, 2, 3);
    let brr = arr.map((item) => item);
    brr instanceof MyArray; // false
    brr instanceof Array; // true
    // 默认情况下
    class MyArray extends Array {
      static get [Symbol.species]() {
        return this;
      }
    }

    值得注意的是,定义 Symbol.species 属性时,前面必须声明是静态的 static 并且要运用 get 取值器。

    4.10、Symbol.split

    Symbol.split 指向 一个正则表达式的索引处分割字符串的方法。 这个方法通过 String.prototype.split() 调用。

    String.prototype.split(separator, limit);
    // 等价于
    separator[Symbol.split](this, limit);
    // 例子
    class Split1 {
      [Symbol.split](str) {
        return `${str} Word`;
      }
    }
    "Hello".split(new Split1()); // Hello Word

    4.11、Symbol.toPrimitive

    Symbol.toPrimitive 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。该函数在调用时,会传递一个字符串参数 hint,表示要转换到的原始值的预期类型。字符串 hint 的类型有三种:'number', 'string', 'default'。

    let obj =
      {
        [Symbol.toPrimitive](hint) {
          switch (hint) {
            case "number":
              return 123;
            case "string":
              return "123";
            case "default":
              return "default";
            default:
              throw new Error();
          }
        },
      } + obj; // 123
    `${obj}`; // '123'
    obj + ""; // "default"

    4.12、Symbol.toStringTag

    Symbol.toStringTag 是一个内置 symbol,它通常作为对象的属性键使用,对应的属性值应该为字符串类型,这个字符串用来表示该对象的自定义类型标签,通常只有内置的 Object.prototype.toString() 方法会去读取这个标签并把它包含在自己的返回值里。通俗点讲就是在 Object.prototype.toString()去判断自定义对象的数据类型时,返回的都是 object,可以通过这个属性来给自定义对象添加类型标签。

    Object.prototype.toString.call('123'); // [object String]
    ...more

    另外一些对象类型则不然,toString() 方法能识别它们是因为引擎为它们设置好了 toStringTag 标签:

    Object.prototype.toString.call(new Map()); // "[object Map]"
    Object.prototype.toString.call(function* () {}); // "[object GeneratorFunction]"
    Object.prototype.toString.call(Promise.resolve()); // "[object Promise]"
    ...more

    当我们自己定义一个类时,调用 Object.prototype.toString()时,由于没有内部定义 toStringTag 标签,所以只能返回"[object Object]"

    class Foo {}
    Object.prototype.toString.call(new Foo()); // "[object Object]"
    // 设置Symbol.toStringTag
    class Foo {
      get [Symbol.toStringTag]() {
        return "Foo";
      }
    }
    Object.prototype.toString.call(new Foo()); // "[object Foo]"

    4.13、Symbol.unscopabless

    Symbol.unscopables 指用于指定对象值,其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称。说白了其属性就是控制,在 with 词法环境中哪些属性会被 with 删除。

    Array.prototype[Symbol.unscopabless];
    // {
    //   copyWithin: true,
    //   entries: true,
    //   fill: true,
    //   find: true,
    //   findIndex: true,
    //   includes: true,
    //   keys: true
    // }

    这里简单的讲解一下 with 函数,with 主要是用来对对象取值的,举个简单的例子:

    let obj = {};
    with (obj) {
      let newa = a;
      let newb = b;
      console.log(newa + newb);
    }
    // 等价于
    let newa = obj.a;
    let newb = obj.b;
    console.log(newa + newb);

    with 的 优点: 当 with 传入的值非常复杂时,即当 object 为非常复杂的嵌套结构时,with 就使得代码显得非常简洁。 with 的缺点: js 的编译器会检测 with 块中的变量是否属于 with 传入的对象, 上述例子为例,js 会检测 a 和 b 是否属于 obj 对象,这样就会的导致 with 语句的执行速度大大下降,性能比较差。

    回归正题,我们举个例子看一下 Symbol.unscopables 属性的作用。

    let obj = {
      foo() {
        return 1;
      }
    }
    with(obj) {
      foo(); // 1
    }
    // 设置了Symbol.unscopables
    let obj = {
      foo() {
        return 1;
      },
      get [Symbol.unscopables]() {
        return {
          foo: true
        }
      }
    }
    with(obj) {
      foo(); // Uncaught ReferenceError: foo is not defined
    }

    设置后报错的原因是因为with已经将obj中的foo方法删除了。

    BigInt类型

    BigInt 是一种特殊的数字类型,它提供了对任意长度整数的支持。

    1、概述

    BigInt 是一个新型的内置类型,主要是为了表达大于 2^53-1 的整数。

    我们定义一个 BigInt 类型的数据时有两种方式,第一个是在数字后面加 n,另外一种是调用 BigInt()方法。

    let theBigInt = 9007199254740991n;
    let alsoHuge = BigInt(9007199254740991); // 9007199254740991n

    当用 typeof 对其进行类型判断时,返回的是 bigint。

    let theBigInt = 9007199254740991n;
    typeof theBigInt; // bigint

    2、运算

    BigInt 支持以下的运算符,+、*-**% ,并且支持除了>>> (无符号右移)之外的 其他位运算符。

    let previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER); // 9007199254740991n
    let maxPlusOne = previousMaxSafe + 1n; // 9007199254740992n
    let maxMinusOne = previousMaxSafe - 1n; // 9007199254740990n
    let multi = previousMaxSafe * 2n; // 18014398509481982n
    let mod = previousMaxSafe % 10n; // 1n

    值得注意的是,BigInt 是不支持单目+运算符的。

    +previousMaxSafe; // Uncaught TypeError: Cannot convert a BigInt value to a number

    主要原因还是 BigInt 无法和 Number 类型直接运算,如果想要运算的话必须在同一个类型上,但是有一点值得注意的是,当 BigInt 转为 Number 类型时,有可能会丢失精度。

    在比较运算符中,BigInt 和 Nunber 类型的之间不是严格相等的。

    10n == 10; // true
    10n === 10; // false

    Number 和 BigInt 是可以进行比较的。

    1n < 2; // true
    2n > 1; // true
    2n >= 2; // true

    3、API

    BigInt 拥有两个静态方法:

    这两个方法均接受两个参数,width:可存储整数的位数。bigint:要存储在指定位数上的整数。

    const max = 2n ** (64n - 1n) - 1n;
    BigInt.asIntN(64, max); // 9223372036854775807n
    
    const max = 2n ** 64n - 1n;
    BigInt.asUintN(64, max); // 18446744073709551615n

    同时 BigInt 还拥有三个实例方法:

    let bigint = 3500n;
    bigint.toLocaleString(); // "3,500"
    bigint.toString(); // "3500"
    bigint.valueOf(); // 3500n

    【相关推荐:javascript视频教程编程视频

    以上就是es6新增的js基本数据类型有哪些的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    自己动手写 PHP MVC 框架:点击学习

    快速了解MVC架构、了解框架底层运行原理

    专题推荐:javascript ES6
    上一篇:es6中的类似于for循环有哪些 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• javascript怎么正则替换非汉字的字符• javascript怎么求数组中的最大奇数• JavaScript怎么输入N个数据求平均数• 一文详解JavaScript中执行上下文与执行栈(图文结合)
    1/1

    PHP中文网