Symbol

文章编写参考 阮一峰《ECMAScript 6 入门》


1. 概述

ES5中一个对象额属性名都是字符串,这很容易造成属性名冲突;比如你在使用别人提供的对象,当你向该对象中添加新的属性的时候就要特别小心,谨防属性重名造成冲突。如果有一种机制,保证每个属性的名称都是独一无二的,那么就不用担心这个问题了。也就是是ES6引入Symbol的原因。

【Symbol是一种数据类型】,表示独一无二的值!

Symbol值是通过Symbol( )函数生成的,也就是说从现在起,对象的属性名称不只是字符串了,多了一种Symbol值,并且保证是独一无二的。

let s = Symbol();
typeof s;
//symbol

上面的代码调用Symbol()函数生成了一个独一无二的Symbol值,typeof的执行结果告诉了我们symbol是一种数据类型。

let s1 = Symbol('Blue');
let s2 = Symbol('Crazy');

console.log(s1);    //Symbol(Blue)
console.log(s2);    //Symbol(Crazy)

上面代码中我们为Symbol传入了一个字符串参数,表示对Symbol值得描述,主要目的是为了区分各自是哪个Symbol值。

【如果Symbol的参数是一个对象,就会调用该对象的toString方法,将其转换成字符串】

const obj = {
  toString() {
    return 'abc';
  }
};
const sym = Symbol(obj);
sym // Symbol(abc)

【注意】Symbol值是独一无二的,就算参数一致也只能说明我们给他赋予的描述信息一致,实际上两个值是不相等的。

let s1 = Symbol('Blue');
let s2 = Symbol('Blue');
console.log(s1 === s2);
//false

【Symbol值不能同其他类型的值进行运算】

var sym = Symbol('My symbol');

"your symbol is " + sym
// TypeError: can't convert symbol to string

`your symbol is ${sym}`
// TypeError: can't convert symbol to string

但是Symbol值可以显示的转换成字符串

let s1 = Symbol('Blue');
console.log(s1.toString());
//Symbol(Blue)

2. Symbol值作为对象属性名

概述中就说了Symbol值提出的目的就是为了避免重复,而对象的属性名又刚好不能重复

let s1 = Symbol('Blue');
//第一种写法
var obj = {};
obj[s1] = 'Blue';

//第二种写法
var obj = {
    [s1]: 'Blue'
}
//第三种写法
var obj = {};
Object.defineProperty(obj, s1, {
    value: 'Blue'
})

//上面的代码都得到同样的结果
obj[s1];    //Blue

上面写了三种写法,第一种写法和第三种都不解释,第二种写法根据对象的扩展,变量作为属性名是应该使用方括号包裹。但是【Symbol作为对象属性时不能用点运算符】,也就是下面这样子的

var obj = {};
obj.s1 = 'Blue';

上面这样的写法仔细想想是要干嘛,这不是将s1看成一个字符串吗,不再是一个Symbol值了,这个应该很好理解哈。

Symbol 类型还可以用于定义一组常量,保证这组常量的值都是不相等的。

log.levels = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn')
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');

下面是另外一个例子。

const COLOR_RED    = Symbol();
const COLOR_GREEN  = Symbol();

function getComplement(color) {
  switch (color) {
    case COLOR_RED:
      return COLOR_GREEN;
    case COLOR_GREEN:
      return COLOR_RED;
    default:
      throw new Error('Undefined color');
    }
}

常量使用 Symbol 值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的switch语句会按设计的方式工作。

还有一点需要注意,Symbol 值作为属性名时,该属性还是公开属性,不是私有属性。


3. 消除魔术字符串

魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。

function getArea(shape, options) {
  var area = 0;

  switch (shape) {
    case 'Triangle': // 魔术字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }

  return area;
}

getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串

上面代码中,字符串Triangle就是一个魔术字符串。它多次出现,与代码形成“强耦合”,不利于将来的修改和维护。

常用的消除魔术字符串的方法,就是把它写成一个变量

var shapeType = {
  triangle: 'Triangle'
};

function getArea(shape, options) {
  var area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 });

上面代码中,我们把Triangle写成shapeType对象的triangle属性,这样就消除了强耦合。

如果仔细分析,可以发现shapeType.triangle等于哪个值并不重要,只要确保不会跟其他shapeType属性的值冲突即可。因此,这里就很适合改用 Symbol 值。

const shapeType = {
  triangle: Symbol()
};

上面代码中,除了将shapeType.triangle的值设为一个Symbol,其他地方都不用修改。


4. 属性名的遍历

Symbol 作为属性名,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个【Object.getOwnPropertySymbols】方法,可以获取指定对象的所有 Symbol 属性名。

【Object.getOwnPropertySymbols方法返回一个数组】,成员是当前对象的所有用作属性名的 Symbol 值。

let s1 = Symbol('Blue');
let s2 = Symbol('Crazy');
let obj = {
    [s1]: 'Blue',
    [s2]: 'Crazy'
}
Object.getOwnPropertySymbols(obj);
//[ Symbol(Blue), Symbol(Crazy) ]

【Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。】

let s1 = Symbol('Blue');
let s2 = Symbol('Crazy');
let obj = {
    [s1]: 'Blue',
    [s2]: 'Crazy',
    age: 23
}
Reflect.ownKeys(obj)
//[ Symbol(Blue), Symbol(Crazy), age ]

因为由Symbol作为属性名称,不会被常规方法遍历到。我们可以利用这一特性,为对象定义一些非私有,但又希望只使用于内部的方法。

var size = Symbol('size');

class Collection {
  constructor() {
    this[size] = 0;
  }

  add(item) {
    this[this[size]] = item;
    this[size]++;
  }

  static sizeOf(instance) {
    return instance[size];
  }
}

var x = new Collection();
Collection.sizeOf(x) // 0

x.add('foo');
Collection.sizeOf(x) // 1

Object.keys(x) // ['0']
Object.getOwnPropertyNames(x) // ['0']
Object.getOwnPropertySymbols(x) // [Symbol(size)]

上面代码中,对象x的size属性是一个 Symbol 值,所以Object.keys(x)、Object.getOwnPropertyNames(x)都无法获取它。这就造成了一种非私有的内部方法的效果。


5. Symbol.for( )和Symbol.keyFor( )

有时,我们希望重新使用同一个Symbol值,Symbol.for方法可以做到这一点。

5.1 Symbol.for( )

Symbol.for( )接受一个字符串作为参数,并且去查找以该参数为名称的Symbol值,【如果找到则返回,找不到则新建】

let s1 = Symbol.for('Blue');
let s2 = Symbol.for('Blue');

s1 === s2;  //true

Symbol( )和Symbol.for( )这两种写法可以返回新的Symbol值。但是Symbol.for( )会在全局中搜索,但是Symbol( )不会。

Symbol.for( )会先检查全局中是否有给定的key存在,如果不存在才创建新值,但是Symbol( )不管,直接创建新值。

//这个会创建30个不同的Symbol值
for (let i = 0; i < 30; i++) {
    Symbol('Blue')
}

//这个只会返回一个值
for (let i = 0; i < 30; i++) {
    Symbol.for('Blue')
}

5.2 Symbol.keyFor()

Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key

let s1 = Symbol.for('Blue');
let s2 = Symbol('Blue');

Symbol.keyFor(s1);  //Blue
Symbol.keyFor(s2);  //undefined

上面代码s2不是有Symbol.for()注册,所以返回undefined。

需要注意的是,Symbol.for为Symbol值登记的名字,是全局环境的,可以在不同的 iframe 或 service worker 中取到同一个值。

iframe = document.createElement('iframe');
iframe.src = String(window.location);
document.body.appendChild(iframe);

iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo')
// true

上面代码中,iframe 窗口生成的 Symbol 值,可以在主页面得到。


03-08
### Symbol in Programming and IT Context In the programming and IT context, a symbol represents an object or entity within code that can be referenced by name. Symbols are fundamental components used across various aspects of software development and system architecture. For instance, when working with binary analysis tools like Angr, symbols refer to named entities defined during compilation such as functions or global variables[^1]. These symbols allow developers to interact more meaningfully with compiled binaries without needing detailed knowledge about internal memory addresses: ```python sym = project.loader.find_symbol('missing_symbol') print(sym) # This command retrieves information regarding 'missing_symbol' ``` Symbols play crucial roles not only in low-level operations but also high-level abstractions including databases where they might represent keys or columns serving specific purposes[^3]. #### Characteristics of Symbols - **Uniqueness**: Within certain scopes, each symbol should have unique identifiers. - **Referential Integrity**: Ensures relationships between different parts remain consistent throughout application lifecycle. - **Descriptive Naming**: Helps improve readability and maintainability of source codes. #### Applications Across Domains Beyond direct coding practices, understanding symbols enhances capabilities in multiple domains: - **Binary Analysis & Reverse Engineering** - **Database Management Systems (DBMS)** - **Compiler Design** By grasping this concept thoroughly, one gains deeper insights into how programs operate internally while facilitating effective debugging sessions alongside enhancing overall productivity.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值