Symbol :每次返回的都是一个新的值;
1.Symbol是ES6引进的一种新的原始数据类型,表示一种独一无二的值;
2.Symbol值通过Symbol函数来生成,这就是说,现在的属性值有两种类型:
2.1 一种是原有的字符串的值;
2.2 一种是新增的Symbol函数的值;
2.3 凡是属性名属于Symbol类型,就是独一无二的值,可以保证不会和其他属性名产生冲突;
example:
let s = Symbol() ;
typeof s ;
在这里的Symbol就是一个独一无二的值 ;
注意:
Symbol函数不能使用new, 因为他不是一个对象,而是一个类似于字符串的值;所以,它不能添加属性;
4.Symbol可以接收一个字符串作为参数,表示对Symbol函数的描述;
这样是为了方便的在控制台里面显示,或者是转换称为字符串时方便输出;
example 1 :
var s1 = Symbol( 'foo1' ) ;
var s1 = Symbol( 'foo2' ) ;
s1 // Symbol( 'foo1' ) ;
s2 // Symbol( 'foo2' ) ;
s1.toString() // "Symbol( 'foo2' )" ;
s2.toString() // "Symbol( 'foo2' )" ;
注意:
上面Symbol()函数生成的值,如果没有添加字符串进行区分,输出的时候就不能区分那个数值是那个数值;
example 2 :
at1: 如果Symbol的值是一个对象,就会自动调用对象的toString方法,将其转换成字符串,
然后才生成一个Symbol值;
const obj = {
toString () {
return 'abc' ;
}
}
const sym = Symbol( obj ) ;
sym // Symbol( obj ) ;
at2: Symbol即使里面传入的参数是一致的,每个Symbol返回的都是一个不一样的值;
没有参数的情况:
var s1 = Symbol() ;
var s2 = Symbol() ;
s1 === s2 ; // false ;
有参数的情况:
var s1 = Symbol( 'foo' ) ;
var s2 = Symbol( 'foo' ) ;
s1 === s2 ; // false ;
5.Symbol值不能与其他类型值进行运算,会报错;
example:
var sym = Symbol( 'my symbol' ) ;
'you symbol id ' + sym ;
但是,Symbol值可以显示转换策划那个字符串 ;
var sym = Symbol( 'my symbol' ) ;
String( sym ) ; // 'Symbol( my symbol )' ;
sym.toString() ; // 'Symbol( my symbol )' ;
6.Symbol也可以转为bool值,但是不能转为数值;
example:
var sym = Symbol() ;
Boolean( sym ) ; // true
!sym ;// false
if ( sym ) {
// 程序是可以执行的。。。
}
Number( sym ) ; // type error
sym + 2 ; // type error
7.Symbol值作为对象的属性名,可以防止我们在编写代码的时候对象的某个键值被修改 ;
var sym = Symbol() ;
var a = {} ;
a[ symbol ] = 'hello' ;
var a = {
[ symbol ]: 'hello' ,
}
Object.defineProperty( a , mySymbol , { value : 'hello!' } ) ;
上面的代码通过方括号结构和defineProperty, 将对象属性名定义为一个Symbol值 ;
Symbol作为对象的属性名时,不能作为点运算符 ;
因为点运算后面的总是字符串,不能取到作为Symbol所标识的值 ;
example:
var mySymbol = Symbol() ;
a.mySymbol ; // 'hello' ;
a[mySymbol] ; // undefined
a[ 'mySymbol' ] ; // 'hello'
同理,在对象内部的时候,使用Symbol值作为一个属性的时候,也需要将其放置在一个方括号里面 ;
const COLOR_GREEN = Symbol() ;
const COLOR_RED = Symbol() ;
function checkColor ( color ) {
switch ( color ) {
case COLOR_GREEN :
return COLOR_RED ;
case COLOR_RED :
return COLOR_RED ;
default throw new Error ( ' undefined color ' ) ;
}
}
需要注意的是Symbol值作为属性的时候,该属性还是公有属性,而不是私有属性 ;
8. Symbol值可以消除代码的强耦合,使代码的结构看起来更加的清晰 ;
下面就是一段强耦合的代码:
function getArea ( shapeType , options ) {
switch ( shapeType ) {
case 'Triangle' :
area = .5 * options.width * options.height ;
break ;
...more code ;
}
return area ;
}
getArea( 'Triangle' , { width: 10 , height: 10 } ) ;
常用的消除字符串的方法,就是把它写成一个变量;
var shapeType = {
triangle : 'triangle' ,
}
function getArea ( area , options ) {
var area = 0 ;
switch ( shape ) {
case shape === 'triangle' :
area = options.width * options.height * .5 ;
break ;
}
return area ;
}
getArea( shapeType.shape , { width: 10 , height: 10 } ) ;
9.Symbol作为一个属性值,不会出现在 for...in , for...of 循环中 ,也不会被Object.keys() ,
Object.getOwnPropertyNames() , JSON.Stringfy()返回 ;
但是,它也不是私有属性,有一个getOwnPropertySymbols()方法,可以获取指定对象的所有Symbol属性名 ;
getOwnPropertySymbols() 方法返回一个数组, 成员是当前对象的所有用作属性名的Symbol值 ;
var obj = {} ;
var a = Symbol( 'a' ) ;
var b = Symbol( 'b' ) ;
obj[a] = 'hello' ;
obj[b] = 'world' ;
var objectSymbols = Object.getOwnPropertySymbols( obj ) ;
objectSymbols ; // Symbol( 'a' ) , Symbol( 'b' ) ;
example:
getOwnPropertyNames() , for...in , getOwnPropertySymbols() 之间的对比 ;
var obj = {} ;
var foo = Symbol( 'foo' ) ;
Object.defineProperty( obj , foo , { value : 'foobar' } ) ;
for ( var i in obj ) { console.log( i ) ; } ;
Object.getOwnPropertyNames( obj ) ; // []
Object.getOwnPropertySymbols( obj ) ; //[Symbol('foo')]
10.Reflect.ownKeys() , 新的API , 方法可以返回所有类型的键名,包括常规的键名和Symbol键名 ;
let obj = {
[Symbol('myKey')] : 1 ,
enum: 2 ,
nonEnum: 3 ,
} ;
11.Symbol.for()
它接收一个字符串作为参数,然后搜索有没有该参数作为名称的Symbol值,如果有就返回这个值,
如果没有就重新新建并返回一个Symbol值;
example:
var s1 = Symbol.for( 'foo' ) ;
var s2 = Symbol.for( 'foo' ) ;
s1 === s2 ; // true ;
上面的代码中,虽然都是Symbol值,但都是通过Symbol.for生成的值,所以它们是相等的;
12.Symbol() , Symbol.for()之间的区别 ;
前者会登记在全局中搜索,后者则不会,不会每次新建一个Symbol值,而是去轮询是否存在,如果存在
则返回已存在的值,不存在才会新建一个新的值 ;
example:
Symbol.for( 'bar' ) === Symbol( 'bar' ) ; // true
Symbol( 'bar' ) === Symbol( 'bar' ) ; // false
13.Symbol.keyFor() ; 每次调用都会返回一个已经存在的Symbol值, 若是不存在则会返回undefined ;
example:
var s1 = Symbol.for( 'foo' ) ;
Symbol.keyFor( s1 ) ; // foo ;
var s2 = Symbol( 'foo' ) ;
Symbol.keyFor( s2 ) ; // undefined ;
14.Symbol.for() 生成的Symbol值是在全局环境中的, 可以在不同的iframe或者server worker中
取得同一个值 ;
var iframe = Document.createElement( 'iframe' ) ;
iframe.src = String( window.location ) ;
document.body.appendChild( iframe ) ;
iframe.contentWindow.Symbol.for( 'foo' ) === Symbol.for( 'foo' ) ;
上面的代码中生成的Symbol值,可以在主页面得到 ;
15.模块的SingleTon模式 ;
该模式调用的是一个类,但是任何时候返回的都是一个实例 ;
对于node来说,模块可以看成是一个类 ,要保证每次执行这个模块文件,生成同一个实例 ,
只要把这个类放置在顶级对象global中 ;
example:
// mod.js
function foo () { this.foo = 'hello' ; }
if ( !global._foo ) { return new foo() ; }
module.exports = global._foo ;
var mod = require( 'mod.js' ) ;
console.log( foo._foo ) ;
但是这里的global._foo每次返回的都是一个实例,但是是可以修改的,为了保证这个这个变量不会失真,
引进Symbol可以达到这样的目标 ;
example:
const FOO_KEY = Symbol.for( 'foo' ) ;
function A () { this.foo = 'hello' ; }
if ( !global[ FOO_KEY ] ) { return new A() ; }
module.exports = global[ FOO_KEY ] ;
上面的代码中,可以保证键值不会被无意间被覆盖,但还是可以被改写 ;
var a = require( './mod.js' ) ;
global[ Symbol.for( 'foo' ) ] = 123 ;
如果键名使用的是Symbol方法生成 ,那么外部将无法引用这个值, 当然也就无法改写 ;
.Symbol.for() 和 Symbol() 每次都会生成新的值;
区别在于 .for 不会主动去创建一个新的Symbol值,每次都会返回同样的值,轮询是否存在,若不存在则会创建新的值;Symbol每次都会返回新的值;