Symbol在项目中如何使用?

大厂技术  高级前端  Node进阶点击上方 程序员成长指北,关注公众号
回复1,加入高级Node交流群

前言

下面的例子只讲 Symbol在实战中的使用,对于基础语法可以参考其他文章,这里就不展开了

主要用到Symbol两个特性:

  1. 每次使用的值都是唯一的

  2. Symbol值作为属性名称时,不能被遍历

唯一值

例子:前端维护一个todoList,每个todoItem需要唯一Id,点击删除按钮,根据todoItem id查找索引删除

<template>
 <div>
   <el-row :gutter="20">
     <el-col :span="15">
       <el-input v-model="newTodo" placeholder="添加新的待办事项"></el-input>
     </el-col>
     <el-col :span="8">
       <el-button @click="addTodo" type="primary">添加待办事项</el-button>
     </el-col>
   </el-row>
   <el-divider></el-divider>
   <el-table :data="todos" style="width: 100%">
     <el-table-column prop="title" label="待办事项"></el-table-column>
     <el-table-column label="操作">
       <template slot-scope="scope">
         <el-button @click="removeTodo(scope.row.id)" type="text" size="medium"
           >移除</el-button
         >
       </template>
     </el-table-column>
   </el-table>
 </div>
</template>

<script>
export default {
 data() {
   return {
     newTodo: "",
     todos: [],
   };
 },
 methods: {
   addTodo() {
     if (this.newTodo.trim() !== "") {
       this.todos.push({
           id: Date.now(),
           title: this.newTodo,
       });
       this.newTodo = "";
     }
   },
   removeTodo(id) {
     this.todos.splice(this.todos.findIndex((todo) => todo.id === id), 1);
   },
 },
};
</script>
934469045b31a949aac5eeeb48de469c.jpeg

当添加todoItem时,需要前端给这个todoItem生成一个唯一 id

放在以前,你可能会想到时间戳,也就是上面代码中的做法

this.todos.push({
   id: Date.now(),
   title: this.newTodo,
});

确实,Date.now()可以保证todoItem每个id都是唯一

8a8877e4fbb27141b4646234ced35040.jpeg

但是如果这里需要同时添加多条todoItem数据喃?

使用Date.now()就会变成这样

this.todos.push(
   ...Array.from(
       {
           length: 2,
       },
       () => ({
           id: Date.now(),
           title: this.newTodo,
       })
   )
);
257bb9352e8b51d1e5e7e19fe678d855.jpeg

但是你会发现todoItemid并不唯一

这很简单,js同步执行时间比1ms还要快,所以两个时间戳是一样的

这个时候你再上网百度,网上说在时间戳后面拼接一个随机数

this.todos.push(
   ...Array.from(
       {
           length: 2,
       },
       () => ({
           id: Date.now() + "_" + Math.floor(Math.random() * 10),
           title: this.newTodo,
       })
   )
);
a21b8e0dea63972f0ef63e2302f7c2b0.jpeg

但是这样真的可以吗?如果同时添加10000个todoItem

3346c629ba61de674b256600e923bc46.jpeg

或许你觉得可以生成更长位数的随机数,但是无论生成多长的随机数都会有相等的可能,再长也不过是自欺欺人罢了!!!

当然使用UUID等唯一值库可以实现上面的需求,但是你是否想过,原生JS真的不能自己实现这个需求吗?

Symbol()就登场了,Symbol()产生的值永远是唯一的,无论同时添加多少个todoItem都不会产生重复id

this.todos.push(
   ...Array.from(
       {
           length: 1000,
       },
       () => ({
           id: Symbol(),
           title: this.newTodo,
       })
   )
);

function hasDuplicates(arr) {
   const ids = new Set();
   return arr.some((item) => ids.size === ids.add(item.id).size);
}

if (hasDuplicates(this.todos)) {
   console.error("有重复");
} else {
   console.log("没有重复");
}

无论是生成1000个还是100000个todoItemid永远不会重复

私有属性

例子:需要封装一个播放器类,在实例化时向外暴露播放和停止方法,通过这些方法去修改播放器类的播放状态属性,并且只允许调用方法修改这个播放状态属性,也就是说这个播放状态属性必须是私有属性,不能向外暴露,防止其他开发者不通过方法调用直接去修改播放状态属性,造成不必要的BUG

// Player.js

const Play_State = Symbol('Play_State');

export default class Player {

   [Play_State] = "Paused"
   
   commonField = "普通字段"

   play() {
       this[Play_State] = "Playing"
   }

   stop() {
       this[Play_State] = "Paused"
   }
}
// index.js

import Player from './Player'

let player = new Player()
6f8eaac2d53722ad1b599a2c20922e0f.jpeg

你可以会觉得可以通过player[Symbol("Play_State")]方式访问到属性值

caad37b4f286fed72d45d7d9b2f0482d.jpeg

但是你会发现,最后返回值永远为空,这是因为每次调用Symbol("Play_State")都会生成唯一的属性名称

不能直接访问,那可以通过遍历对象访问到吗?

// for in
for (const key in player) {
 console.log(key, player[key])
}

// for of
for (const key of Object.keys(player)) {
 console.log(key, player[key])
}
4c384981da1b9cc30efaf8b9392d484b.jpeg

只能遍历出普通属性,但万事不是绝对的,JS还是提供了遍历Symbol属性的方法的

for (const key of Object.getOwnPropertySymbols(player)) {
 console.log(key, player[key])
}
a27e62e44474846e8c74ce525ab9d1cf.jpeg

但这并不代表使用Symbol属性当作私有属性是错误的,毕竟一般开发也不会使用Object.getOwnPropertySymbols获取对象属性呀~~~

全局唯一

在控制台输出一个空数组

51df01c0962956ce5261211907d39af1.jpeg

在数组的原型上面Symbol(Symbol.iterator)Symbol(Symbol.unscopables)等方法

如何访问这些方法喃?直接([]).[Symbol(Symbol.iterator)]显然是不行的,和上面私有属性原理是一样的

要想了解如何访问这些方法,先在控制台打印一下Symbol

1f254600faccc2960a35923217a023c4.jpeg

Symbol.iterator的属性值好像和数组原型上面的Symbol(Symbol.iterator)方法名称是一样

验证一下

837587deae9b0c3f3ea61df1d4ce97a7.jpeg

果然如我们所想,那么就可以想到JS编译器在数组上定义Symbol(Symbol.iterator)是如何实现的

Object.defineProperty(Symbol, "iterator", {
 value: Symbol("Symbol.iterator"),
});

Array.prototype[Symbol.iterator] = function () {}

这样做的好处是,以后无论在Array.prototype添加什么方法和属性,都不会干扰到Symbol(Symbol.iterator)方法

Node 社群



我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。

   “分享、点赞、在看” 支持一波👍
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值