闽公网安备 35020302035485号
let user = {
id:'1',
name:'Jack'
}
// 假设有个第三方库里面的方法
function foo(obj) {
obj.id = '2'
}
foo(user);
console.log(user)
这种情况,user的id就被意外修改了,因为对象的属性的key默认都是字符串,是很容易被覆盖的。 为了避免这种情况,可以用Symbol。代码如下:let userId = Symbol();
let user = {
[userId]:'1',
name:'Jack'
}
// 堆代码 duidaima.com
// 假设有个第三方库里面的方法
function foo(obj) {
obj.id = '2'
}
foo(user);
console.log(user[userId]) // 还是1
我们定义了一个Symbol对象,作为user的属性。由于Symbol是不可变的,这样就保证了对象属性的安全性。另外,Symbol属性不能用点号去访问,只能用中括号。 但是,如果这个对象被传到其他组件里面去了,并且访问不到userId这个指针,该怎么办?function getUser() {
let userId = Symbol();
let user = {
[userId]:'1',
name:'Jack'
}
return user;
}
function foo(user) {
//怎么在这里拿到id?
console.log(user[Symbol()]) //这样拿到的是undefined
}
foo(getUser());
解决:Symbol.for 可以获取同一个Symbol对象。function getUser() {
let user = {
[Symbol.for("userId")]:'1',
name:'Jack'
}
return user;
}
function foo(user) {
//怎么在这里拿到id?
console.log(user[Symbol.for('userId')]) //这样拿到的是1
}
foo(getUser());
Symbol.for()会有一个登记机制,使用for只会对通过for创建的symbol进行检查,不会对Symbol()创建的进行检查。 也就是说,下面这两个是不相等的。let a = Symbol('a');
let a1 = Symbol.for('a')
console.log(a === a1) // false
如果我想根据Symbol.for创建的变量,拿到里面的描述信息,可以用Synbol.keyfor ,注意Symbol创建的对象,还是拿不到描述信息的。console.log(Symbol.keyFor(a)) // undefined console.log(Symbol.keyFor(a1)) // a总结:Symbol是一个唯一值,如果你不想复用同一个Symbol,就用Symbol创建,如果想复用,就用Symbol.for,如果你还想拿到Symbol.for的描述信息,就用Symbol.keyFor。
class Person {
constructor(name) {
this.name = name;
}
}
let p = new Person('Jack');
console.log(p.name)
name是实例属性,外头可以直接访问到,不能像强类型语言那样有private。但是我们可以利用Symbol唯一的特性,模拟私有属性。const nameSymbol = Symbol('name')
class Person {
constructor(name) {
this[nameSymbol] = name;
this.age = 18; //为了演示实例属性
}
getName() {
return this[nameSymbol]
}
}
// 假设在其他地方用到了person,但是访问不到name
let p = new Person('Jack');
console.log(p.getName()) // Jack
在外头你无法用for in和Object.getOwnPropertyNames(),甚至JSON.stringify来获取nameSymbol。// 假设在其他地方用到了person,但是访问不到name
let p = new Person('Jack');
for (const pKey in p) {
console.log(pKey) //只拿到一个age
}
console.log(Object.getOwnPropertyNames(p)) // ['age']
console.log(JSON.stringify(p)) // {"age":18}
//但是,你依然可以用
let ownPropertySymbols = Object.getOwnPropertySymbols(p);
let symbol = ownPropertySymbols[0];
console.log(p[symbol]) // Jack
类似的,在egg.js源码中,也有这样的用法:const CALL = Symbol('BaseContextLogger#call');
class BaseContextLogger {
[CALL](method, args) {
// ...
this.ctx.logger[method](...args);
}
}
即在当前作用域内是有效的,但是最后export出去的肯定不包含CALL,所以就相当于是private了。 这种用法其实就是用到了Symbol的唯一性,只有当你拿到同一个Symbol对象的时候,才能取到对象里面这个属性的值。只要你不主动把Symbol导出,外界就永远无法访问。在框架里面,或者多模块开发的时候,如果你想在当前模块里面去修改原型方法,又不是改变全局,就比较实用了。但是我们业务开发几乎用不到。let status = {
AWAIT_SP : Symbol.for("1") , //待审批
PASS : Symbol.for("2") , // 审批通过
REJECT : Symbol.for("3") , // 审批退回
}
console.log(Symbol.keyFor(status.AWAIT_SP)) // 1
3) 实现一个可遍历的对象class Person {
constructor(name) {
this[nameSymbol] = name;
this.age = 18; //为了演示实例属性
this.sex = '男' //为了演示实例属性
}
getName() {
return this[nameSymbol]
}
}
let p = new Person('Jack');
for (let item of p) {
console.log(item)
}
因为没有Symbol.iterator,所以会报错图片改造一下:class Person {
constructor(name) {
this[nameSymbol] = name;
this.age = 18; //为了演示实例属性
this.sex = '男' //为了演示实例属性
this.hobbies = ['吃饭','睡觉','打豆豆']
this.index = 0;
}
[Symbol.iterator] () {
return {
next: () => {
if(this.index > this.hobbies.length - 1) {
return { done: true, value: undefined };
}
return { done: false, value: this.hobbies[this.index++] };
}
}
}
getName() {
return this[nameSymbol]
}
}
let p = new Person('Jack');
for (let item of p) {
console.log(item)
}
Symbol.iterator方法需要返回一个包含next方法的对象,然后next方法必须返回一个特定的对象,里面有done和value属性,这些都是约定好的。结果如下: