【TypeScript】Symbol 类型

在 TypeScript (TS) 中,Symbol 是一种基本数据类型,用于创建独一无二的标识符。它是 ECMAScript 6(ES6)引入的新特性,并且也被 TypeScript 支持。Symbol 可以用于创建对象属性、类成员、和其他上下文中,以确保它们的唯一性,从而避免命名冲突和混淆。

下面是一些关于在 TypeScript 中使用 Symbol 的基本信息:

Symbol 的基本使用

  1. Symbol 的基本使用:
    使用 Symbol() 函数可以创建一个新的 Symbol。每个 Symbol 都是唯一的,不能通过常规的方法进行比较。

     let a1:symbol = Symbol(1)
     let a2:symbol = Symbol(1)
     // console.log(a1 === a2)  // false
     // console.log(a1 == a2)  // false 因为内存地址都是不同的
     // Symbol.for 去全局 symbol 有没有注册过这个 key,如果有直接拿来用,没有就去创建一个
     // console.log(Symbol.for('a') === Symbol.for('a')); // true
     
     let obj = {
       name: 1,
       a1: 111,
       a2: 222
     }
     // console.log(obj); // { name: 1, a1: 111, a2: 222 }
     
     let obj2 = {
       name: 1,
       [a1]: 111, // 使用索引签名
       [a2]: 222
     }
     console.log(obj2); // { name: 1, [Symbol(1)]: 111, [Symbol(1)]: 222 }
     
     // for in 不能读到 Symbol
     for(let key in obj) {
       console.log(key);  // name
     }
     // keys 读不到 Symbol,只能读到 name
     console.log(Object.keys(obj));
     // getOwnPropertyNames 读不到 Symbol,只能读到 name
     console.log(Object.getOwnPropertyNames(obj));
     // getOwnPropertySymbols 读到 Symbol,但又读不到 name 了
     console.log(Object.getOwnPropertySymbols(obj));
     // Reflect.ownKeys 可以读到 Symbol 和 name
     console.log(Reflect.ownKeys(obj));
    
    
  2. Symbol 用作对象属性:
    Symbol 可以用作对象的属性名,以避免属性名冲突。因为 Symbol 是唯一的,所以不会意外覆盖其他属性。

    const myObj = {
      [mySymbol]: "This is a Symbol property"
    };
    
    console.log(myObj[mySymbol]); // Output: This is a Symbol property
    
  3. 内置 Symbols:
    TypeScript 和 JavaScript 提供了一些预定义的内置 Symbols,例如 Symbol.iteratorSymbol.toStringTag 等,用于指定对象的特定行为或元信息。

  4. Symbol 作为类成员:
    Symbol 可以用作类的成员,用于定义私有成员或特定的行为。

    const _privateSymbol = Symbol("private");
    
    class MyClass {
      private[_privateSymbol] = "This is a private symbol property";
    }
    
    const instance = new MyClass();
    console.log(instance[_privateSymbol]); // Output: This is a private symbol property
    
  5. Symbols 作为属性键的限制:
    使用 Symbol 作为属性键可以在一定程度上限制外部对属性的访问。但并不是绝对的私有性,因为使用 Object.getOwnPropertySymbols() 仍然可以获取到对象的 Symbol 属性。

    const obj = {
      [mySymbol]: "This is a Symbol property"
    };
    
    const symbolKeys = Object.getOwnPropertySymbols(obj);
    console.log(obj[symbolKeys[0]]); // Output: This is a Symbol property
    

生成器

function * generator() {
  yield Promise.resolve('xx')
  yield 1;
  yield 2;
  yield '3';
}
const man = generator();
console.log(man.next()); // { value: Promise { 'xx' }, done: false }
console.log(man.next()); // { value: 1, done: false }
console.log(man.next()); // { value: 2, done: false }
console.log(man.next()); // { value: '3', done: false }
console.log(man.next()); // { value: undefined, done: true }

迭代器

将迭代器之前先讲几个数据类型:

// Set
let set:Set<number> = new Set([1,1,2,2,3,3])
console.log(set); // Set(3) { 1, 2, 3 }

// Map
let map:Map<any,any> = new Map()
let Arr = [1,2,4]
map.set(Arr,'xx')
// 这里是将 Arr 数组当作 map 元素的 key,'xx' 是 value 插入到 map 中,所以在获取数据的时候用的是 Arr
console.log(map.get(Arr)); // xx

// 伪数组
function args() {
  console.log(arguments);
}
// let list = document.querySelectorAll('div'); // 需要浏览器环境,node 环境不支持

想要遍历以上等等数据类型,就要用到迭代器

let set:Set<number> = new Set([1,1,2,2,3,3])
let map:Map<any,any> = new Map()
let Arr = [1,2,4]
map.set(Arr,'xx')
const each = (value:any) => {
  let i:any = value[Symbol.iterator]()
  let next:any = {done:false}
  while(!next.done){
    next = i.next()
    if(!next.done) {
      console.log(next.value)
    }
  }
}
each(map) // [ [ 1, 2, 4 ], 'xx' ]  输出 key value 的数组
each(set) // 1 2 3

迭代器的语法糖

// for of 对象不可以使用
let set:Set<number> = new Set([1,1,2,2,3,3])
for (let value of set) {
  console.log(value); // 1 2 3
}

解构的底层原理也是调用了迭代器

let [a,b,c] = [1,2,3]
console.log(a,b,c); // 1 2 3
let arr = [2,3,4]
let copy = [...arr]
console.log(copy); // [2, 3, 4]

在迭代器的语法糖中提到对象不可以使用 for of ,那么对象就始终不可以使用 for of 吗?no!我们可以用调用的Iterator接口的方式解决这个问题:一个对象如果要具备可被for...of循环调用的 Iterator 接口,就必须在Symbol.iterator的属性上部署遍历器生成方法。

// 对象遍历的底层原理 迭代器
let obj = {
  max: 5,
  current: 0,
  // 创建迭代器对象,使得 obj 变成了可迭代的
  [Symbol.iterator]() {
    return {
      // 迭代器对象的作用域不同于外部对象,它需要自己存储这些值
      max: this.max,
      current: this.current,
      // 定义了如何生成下一个迭代值
      next() {
        if (this.current < this.max) {
          this.current++
          return {
            done: false,
            value: this.current
          }
        } else {
          return {
            done: true
          }
        }
      }
    }
  }
}
for (let value of obj) {
  console.log(value); // 1 2 3 4 5
}

此时对象也可以使用 for of 啦~

同理,数组解构底层也是调用了 Symbol.iterator

let x = [...obj]
console.log(x); // [ 1, 2, 3, 4, 5 ]

你可能感兴趣的:(TypeScript,typescript,ubuntu,javascript)