依赖文件地址 :https://github.com/chanceLe/ES6-Basic-Syntax/tree/master/js
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>[es6]-09-Symbol</title> 6 <script src="./js/browser.js"></script> 7 <script type="text/babel"> 8 /* 9 * es5的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了别人 10 * 提供的对象,但又想为该对象添加新属性(mixin模式),新方法的名字就有可能 11 * 和现有的方法产生冲突。如果有一种机制,保证每个属性的名字都独一无二就好了, 12 * 这样就从根本上防止属性名的冲突,这就是ES6引入Symbol的原因。 13 * 14 * ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是js的第七种数据类型, 15 * 前六种分别是 undefined null String bool Number Object (数组都是Object类型)。 16 * 17 * Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来 18 * 就有的字符串,一种是新增的Symbol类型。凡是属性名是Symbol类型,就都是独一无二的, 19 * 保证不产生冲突。 20 */ 21 let s = Symbol(); 22 console.log(typeof(s)); //symbol 23 24 //Symbol函数前不能使用new命令,因为生成的Symbol是一个原始类型的值,而不是对象,也就是 25 //说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。 26 27 //Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要为了在控制台显示,或者 28 //转为字符串的时候,比较容易区分。 29 30 let s1 = Symbol(); 31 let s2 = Symbol(); 32 console.log(s1 == s2); //false 33 34 let s3 = Symbol("foo"); 35 let s4 = Symbol("foo"); 36 console.log(s3 == s4); //false 37 38 //返回的是唯一的,且Symbol函数的参数只是起到区分说明的作用。 39 //Symbol值不能与其他类型值运算,否则会报错。 40 41 //但可以显式转为字符串 42 var sym = Symbol("My symbol"); 43 console.log(String(sym)); //"Symbol(My symbol)" 44 console.log(sym.toString()); //“Symbol(My symbol)” 45 46 //Symbol值也可以转为布尔值,但是不能转为数值。 47 console.log(Boolean(sym)); //true 48 console.log(!sym); //false 49 50 /* 51 * 由于每个Symbol值都是不相等的,这意味着可以作为标识符,用于对象的属性名,就可以保证不会出现同名的 52 * 属性,这对一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。 53 * 54 */ 55 var mySymbol = Symbol(); 56 //1 57 var a = {}; 58 a[mySymbol] = "hello!"; 59 //2 60 var a = { 61 [mySymbol]:"hello!" 62 } 63 //3 64 var a = {}; 65 Object.defineProperty(a,mySymbol,{value:"hello!"}) 66 //以上三种写法都得到同样的结果 67 68 console.log(a[mySymbol]); //hello! 69 70 // Symbol值作为对象的属性名时,不能用点运算符。 71 var b = {}; 72 b.mySymbol = "hello!"; 73 console.log(b[mySymbol]); //undefined 74 console.log(b["mySymbol"]); //hello! 75 //因为点运算符后面总是字符串,所以不会读取mySymbol作为标识名指代的那个值。 76 //导致b的属性名实际上是一个字符串,而不是一个Symbol值。 77 78 //Symbol还可以定义一组常量,保证这组常量的值都是不相等的。 79 /* 80 * 常量使用symbol最大的好处就是其他任何值都不可能有相同的值了 81 * 还有一点,Symbol值作为属性名时,该属性还是公开属性,不是私有属性。 82 * 83 * 魔术字符串 84 * 指的是,在代码之中多次出现,与代码形成强耦合的某一个具体的字符串或者数值, 85 * 风格良好的代码应该尽量消除魔术字符串。改由含义清晰的变量代替。 86 * 常用的消除魔术字符串的方法,就是把它写成一个变量。 87 * 88 * 89 * 属性名的遍历 90 * Symbol值作为属性名,该属性不会出现在for...in和for...of循环中,也不会被 91 * Object.keys() Object.getOwnPropertyNames() 中,但它不是私有属性。 92 * 有一个Object.getOwnPropertySymbols()方法,可以获取指定对象的所有Symbol属性。 93 */ 94 var obj = {}; 95 var a = Symbol("a"); 96 var b = Symbol("b"); 97 obj[a] = "hello!"; 98 obj[b] = "world!"; 99 var res = Object.getOwnPropertySymbols(obj); 100 console.log(res); 101 102 //另一个新的API,Reflect.ownKeys()方法可以返回所有类型的键名。包括Symbol。 103 let obj1 = { 104 [Symbol("my_key")]:1, 105 enum:2, 106 nonEnum:3 107 } 108 console.log(Reflect.ownKeys(obj1)); //所有属性名,包括Symbol类型的。 109 110 /* 111 * 有时,我们需要使用同一个Symbol值,Symbol.for可以 做到。接受一个字符串作为参数,然后搜索 112 * 有没有以该参数作为名称的Symbol值。如果有就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的 113 * Symbol值。 114 */ 115 var s5 = Symbol.for("foo"); 116 var s6 = Symbol.for("foo"); 117 console.log(s5 == s6); //true 118 //上面代码,两个都是Symbol值,但是他们都是同样参数的Symbol.for()方法生成的,所以实际上是同一个值。 119 120 /* 121 * Symbol.for()和Symbol()这两个写法都会生成新的Symbol。他们的区别是,前者会被登记在全局环境中供 122 * 搜索,后者不会。Symbol.for()不会每次调用就返回一个新的Symbol类型的值,而是会先检查给定的key是否存在 123 * 如果不存在,才会创建新值。 124 * 125 * Symbol.keyFor()返回一个已登记的Symbol类型值的key。 126 */ 127 var s7 = Symbol.for("foo"); 128 console.log(Symbol.keyFor(s7)); //"foo" 129 130 var s8 = Symbol("foo"); 131 console.log(Symbol.keyFor(s8)); //undefined 因为没用for登记 132 133 //要注意的是,Symbol.for()为Symbol值登记的名字,是全局环境的,可以在不同的iframe或service worker 134 //中取到同一个值。 135 136 /* 137 * 模块的Singleton模式 138 * 是指调用一个类,任何时候返回的都是同一个实例。 139 * 这个返回时的变量名可能会被覆盖,因此可以用symbol来标识。 140 * 141 * 142 * 除了自定义自己使用的Symbol外,es6还提供了11个内置的Symbol值,指向语言内部使用的方法。 143 * 144 * 1.Symbol.hasInstance 指向一个内部方法,当其他对象使用instanceof运算符,判断是否为该对象的实例时, 145 * 调用该方法。 146 */ 147 class myClass{ 148 [Symbol.hasInstance](foo){ 149 return foo instanceof Array; 150 } 151 } 152 console.log( 153 [1,2,3] instanceof new myClass()); //true 154 //instanceof 运算符会触发Symbol.hasInstance方法。 155 156 /* 157 * 2.Symbol.isConcatSpreadable 等于一个布尔值,表示该对象使用Array.prototype.concat()时,是否 158 * 可以展开。 159 */ 160 let arr1 = ["c","d"]; 161 console.log(["a","b"].concat(arr1,"e")); //["a","b","c","d","e"] 162 console.log(arr1[Symbol.isConcatSpreadable]); //undefined 163 164 let arr2 = ["c","d"]; 165 arr2[Symbol.isConcatSpreadable] = false; 166 console.log(["a","b"].concat(arr2,"e")); //["a","b",["c","d"],"e"] 167 168 /* 169 * 上面的代码表明,数组的默认行为是可以展开的。Symbol.isConcatSpreadable属性等于true或undefined, 170 * 都有这个效果。 类似数组的对象也可以展开,但Symbol.isConcatSpreadable属性默认为false,必须手动打开。 171 */ 172 let obj2 = {length:2,0:"c",1:"d"}; 173 console.log(["a","b"].concat(obj2,"e")); //["a","b",object,"e"] 174 175 obj2[Symbol.isConcatSpreadable] = true; 176 console.log(["a","b"].concat(obj2,"e")); //["a","b","c","d","e"] 177 178 //对于一个实例来说,Symbol.isConcatSpreadable属性必须写成实例的属性: 179 // this[Symbol.isconacatSpreadable]=true; 180 181 //其余方法,不太常用,暂时放放。 182 </script> 183 </head> 184 <body> 185 </body> 186 </html>