es6新增的js基本資料型別有哪些

2022-10-17 18:00:07

es6新增的基本資料型別:1、Symbol型別,表示獨一無二的值,即Symbol範例是唯一、不可變的;它的產生是因為要用來唯一的標記,進而用作非字串形式的物件屬性,是確保物件屬性使用唯一識別符號,不會發生屬性衝突的危險。2、BigInt型別,提供對任意長度整數的支援,主要是為了表達大於「2^53-1」的整數。

前端(vue)入門到精通課程:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:

本教學操作環境: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)

最開始看到這兩個方法時,我以為是兩個遍歷的方法。

  • Symbol.for(key):使用給定的 key 搜尋現有的 symbol,如果找到則返回該 symbol。否則將使用給定的 key 在全域性 symbol 登入檔中建立一個新的 symbol。

  • Symbol.keyFor(sym):從全域性 symbol 登入檔中,為給定的 symbol 檢索一個 key。

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 擁有兩個靜態方法:

  • BigInt.asIntN(width, bigint):將 BigInt 值轉換為一個-2^width-1 與 2^width-1-1 之間的有符號整數。

  • BigInt.asUintN(width, bigint):將一個 BigInt 值轉換為 0 與 2^width-1 之間的無符號整數。

這兩個方法均接受兩個引數,width:可儲存整數的位數。bigint:要儲存在指定位數上的整數。

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

const max = 2n ** 64n - 1n;
BigInt.asUintN(64, max); // 18446744073709551615n
登入後複製

同時 BigInt 還擁有三個實體方法:

  • BigInt.prototype.toLocaleString():返回此數位的 language-sensitive 形式的字串。覆蓋 Object.prototype.toLocaleString() 方法。

  • BigInt.prototype.toString():返回以指定基數(base)表示指定數位的字串。覆蓋 Object.prototype.toString() 方法。

  • BigInt.prototype.valueOf():返回指定物件的基元值。 覆蓋 Object.prototype.valueOf() 方法。

let bigint = 3500n;
bigint.toLocaleString(); // "3,500"
bigint.toString(); // "3500"
bigint.valueOf(); // 3500n
登入後複製

【相關推薦:、】

以上就是es6新增的js基本資料型別有哪些的詳細內容,更多請關注TW511.COM其它相關文章!