我们知道对象的属性键只可以是Symbol和字符串,不可以是Number也不可以是Boolean。现在我们来讲一讲Symbol类型
Symbol
1.使用场景
假如我有两组有关联的数据,一组是用户信息、另外一组是用户的购物车数据,这时我把它存放在一个对象中,这时如果某个用户信息为car且购物车的某个数据也为car,我们在获取数据时就只会获取到最先获取到的数据
解决方法:
1.给数据属性加上前缀后缀
2.使用Symbol类型
Symbol保证是唯一的。
2.声明方式
let id = Symbol();
//创建时,我们可以给Symbol一个描述
let id = Symbol("id");
//第三种方法
let id = Symbol.for("id1");
//即使我们创建了许多相同描述的Symbol,他们的
//值也是不同的,描述只是一个标签,不会影响任何东西
let id1 = Symbol("id");
let id2 = Symbol("id");
alert(id1 == id2);//返回false
//但是如果我们我们用第三种方法来定义
let id1 = Symbol.for("id");
let id2 = Symbol.for("id");
alert(id1 == id2);//返回true
正如我们所看到的,通常所有的 Symbol 都是不同的,即使它们有相同的名字。但有时我们想要名字相同的 Symbol 具有相同的实体。例如,应用程序的不同部分想要访问的 Symbol “id” 指的是完全相同的属性。
为了实现这一点,这里有一个 全局 Symbol 注册表。我们可以在其中创建 Symbol 并在稍后访问它们,它可以确保每次访问相同名字的 Symbol 时,返回的都是相同的 Symbol。
要从注册表中读取(不存在则创建)Symbol,请使用 Symbol.for(key)。
当用Symbol.for时相当于返回一个引用,而两个id1与id2为指向同一个对象的引用,所以最后一个alert才可以返回true
Symbol.keyFor
对于全局 Symbol,不仅有 Symbol.for(key) 按名字返回一个 Symbol,还有一个反向调用:Symbol.keyFor(sym),它的作用完全反过来:通过全局 Symbol 返回一个名字。
// 通过 name 获取 Symbol
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");
// 通过 Symbol 获取 name
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
3.使用symbol解决字符串耦合问题
当我们在对象中声明两个一样的属性时,后声明的属性会覆盖掉先声明的属性。在实际情况中,会出现难免会出现名称相同的情况,为了避免这种问题,可以在声明时设置一个为symbol类型的key值,这样就可以避免这个问题了。
//一个班有两个叫小明的同学,他们的成绩不一样
let grade = {
ming: { js:100, css:89},
ming: { js:35, css:55},
};
console.log(grade.ming);//{js: 35, css: 55}
//第一个小ming被第二个小ming覆盖了
let user1 = 'ming';
let user2 = 'ming';
let grade = {
[user1]: { js:100, css:89},
[user2]: { js:35, css:55},
};
console.log(grade);//js:35 css:55然而不是我们想要的
let user1 = {
name: "ming",
key: symbol(),
};
let user2 = {
name: "ming",
key: symbol(),
};
let grade = {
[user1.key]: { js:100, css:89},
[user2.key]: { js:35, css:55},
};
console.log(grade[user1.key]);
//js:100,css:89通过symbol我们得到了我们想要的了。
4.Symbol在缓存容器中的使用
这里我们先创建一个容器,之后是简单的读写操作
假设这个容器里面储存了一个用户名为lemon的用户信息
class Cache {
static data = {};
static set(name, value) {
return this.data[name] = value;
}
static get(name) {
return this.data[name];
}
}
Cache.set('user',"lemon");
console.log(Cache.get('user'));//lemon
当我们想要往这个容器里面再写入一个物品信息而这个物品也是lemon时,后一个lemon就会将前一个lemon覆盖
let user = {
name: "lemon",
describ: "用户资料"
};
let cart = {
name: "lemon",
describ: "购物车"
}
Cache.set("lemon", user);
Cache.set("lemon", cart);
console.log(Cache.get("apple"));//输出的是购物车的信息
普通情况下我们可以为lemon加上前缀或者后缀即可
但是在像是多人协作的情况下会出现加前缀后缀还是会重复出现的情况。
这时就需要symbol来避免数据之间的覆盖问题
let user = {
name: "lemon",
describ: "用户资料",
key: Symbol(""),
};
let cart = {
name: "lemon",
describ: "购物车",
key: Symbol(""),
}
Cache.set("user.key", user);
Cache.set("cart.key", cart);
console.log(Cache.get("user.key"));
console.log(Cache.get("cart.key"));
5.“隐藏”属性
Symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。
例如,如果我们使用的是属于第三方代码的 user 对象,我们想要给它们添加一些标识符。
我们可以给它们使用 Symbol 键:.
let user = { // 属于另一个代码
name: "John"
};
let id = Symbol("id");
user[id] = 1;
alert( user[id] ); // 我们可以使用 Symbol 作为键来访问数据
使用 Symbol(“id”) 作为键,比起用字符串 “id” 来有什么好处呢?
因为 user 对象属于其他的代码,那些代码也会使用这个对象,所以我们不应该在它上面直接添加任何字段,这样很不安全。但是你添加的 Symbol 属性不会被意外访问到,第三方代码根本不会看到它,所以使用 Symbol 基本上不会有问题。
另外,假设另一个脚本希望在 user 中有自己的标识符,以实现自己的目的。这可能是另一个 JavaScript 库,因此脚本之间完全不了解彼此。
然后该脚本可以创建自己的 Symbol(“id”),像这样:
let id = Symbol("id");
user[id] = "Their id value";
我们的标识符和它们的标识符之间不会有冲突,因为 Symbol 总是不同的,即使它们有相同的名字。
……但如果我们处于同样的目的,使用字符串 “id” 而不是用 symbol,那么 就会 出现冲突:
let user = { name: "John" };
// 我们的脚本使用了 "id" 属性。
user.id = "Our id value";
// ……另一个脚本也想将 "id" 用于它的目的……
user.id = "Their id value"
// 砰!无意中被另一个脚本重写了 id!
对象字面量中的 Symbol
如果我们要在对象字面量 {…} 中使用 Symbol,则需要使用方括号把它括起来。
就像这样:
let id = Symbol("id");
let user = {
name: "John",
[id]: 123 // 而不是 "id":123
};
这是因为我们需要变量 id 的值作为键,而不是字符串 “id”。
Symbol 在 for…in 中会被跳过
Symbol 属性不参与 for…in 循环。
let id = Symbol("id");
let user = {
name: "John",
age: 30,
[id]: 123
};
for (let key in user) alert(key); // name, age (no symbols)
// 使用 Symbol 任务直接访问
alert( "Direct: " + user[id] );
这是一般“隐藏符号属性”原则的一部分。如果另一个脚本或库遍历我们的对象,它不会意外地访问到符号属性。
相反,Object.assign 会同时复制字符串和 symbol 属性:
let id = Symbol("id");
let user = {
[id]: 123
};
let clone = Object.assign({}, user);
alert( clone[id] ); // 123
这里并不矛盾,就是这样设计的。这里的想法是当我们克隆或者合并一个 object 时,通常希望 所有 属性被复制(包括像 id 这样的 Symbol)。
6.总结
symbol是唯一标识符的原始类型
Symbol 总是不同的值,即使它们有相同的名字。如果我们希望同名的 Symbol 相等,那么我们应该使用全局注册表:Symbol.for(key) 返回(如果需要的话则创建)一个以 key 作为名字的全局 Symbol。使用 Symbol.for 多次调用 key 相同的 Symbol 时,返回的就是同一个 Symbol。
Symbol 有两个主要的使用场景:
“隐藏” 对象属性。
如果我们想要向“属于”另一个脚本或者库的对象添加一个属性,我们可以创建一个 Symbol 并使用它作为属性的键。Symbol 属性不会出现在 for…in 中,因此它不会意外地被与其他属性一起处理。并且,它不会被直接访问,因为另一个脚本没有我们的 symbol。因此,该属性将受到保护,防止被意外使用或重写。
因此我们可以使用 Symbol 属性“秘密地”将一些东西隐藏到我们需要的对象中,但其他地方看不到它。
JavaScript 使用了许多系统 Symbol
这些 Symbol 可以作为 Symbol.* 访问。我们可以使用它们来改变一些内置行为。例如,在本教程的后面部分,我们将使用 Symbol.iterator 来进行 迭代 操作,使用 Symbol.toPrimitive 来设置 对象原始值的转换 等等。(将在下一篇笔记讲到)
如有帮助,请鼓励一下