先来看看 JS传统的数据类型
传统 JS 中数据类型分为 基本类型 和 引用类型 (1)基本(原始)类型
Number类型Boolean类型String类型nullundefined基本类型变量间的相互复制
var a = 10; //开辟一块内存空间保存变量a的值“10”(基本类型); var b = a; //给变量 b 开辟一块新的内存空间,将 a 的值 “10” 赋值一份保存到新的内存里;这时 a 与 b 指向两个不同的地址上述代码中,a 和 b 的值以后无论如何变化,都不会影响到对方的值; 直接原因就是因为,当声明变量保存基本类型的值时,值会直接保存在栈内存中,没有了地址的指向,自然就是不会改变 (2)引用类型
对象(函数,数组)基本类型变量间的相互复制
var a = {i: 1,q: 1}//这个时候 a 的值是一个地址,指向堆内存,而值储存在堆内存,a 根据地址来提取值 var b = a // 将 a 的指引地址赋值给 b,所以b 和 a 现在指向同一地址; a.i = 2 a.q= 2 console.log(b) //{i: 2,q: 2} // 这个时候通过 a 来修改对象的属性,则通过 b 来查看属性时对象属性已经发生改变;注意:如果直接修改整个对象,JS 会给变量重新在堆内存中存值,并改变指向(地址)
var a = {i: 1,q: 1} //同上 var b = a //同上 var a = {i: 5,q: 10} //给 a 赋予新对象 console.log(b) //{i: 1,q: 1}上面的第三行代码执行后,输出 b 的值,并没有被改变。 原因是因为,当 a 被重新赋予对象时,a 指向的地址已经改变,变成堆内存中储存新值的地址,然而这时,b 的地址还是指向的之前的地址,所以 b 的值不被改变。
Symbol 值通过 Symbol( ) 函数生成。Symbol( ) 函数返回的是 Symbol(基本) 类型的值. 这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
1.Symbol(description) description是一个可选的参数,是一个字符串,用来描述 Symbol 实例 description参数的实际作用,主要是为了在控制台显示,或者转为字符串时,比较容易区分。 ES9 提供了一个实例属性description,直接返回 Symbol 的描述(参数)。没有参数则返回undefined
var sym1 = Symbol(); var sym2 = Symbol('foo'); var sym3 = Symbol('foo'); console.log(sym1,sym2,sym3) //Symbol(),Symbol(foo),Symbol(foo) console.log( sym1.description ,sym2.description ,sym3.description) //undefined,foo,foo2.每一个Symbol() 函数返回的值都是唯一的,哪怕它们有相同的参数或无参数。
let s = Symbol(); var a = Symbol() console.log(s == a) //false3.Symbol函数前不能使用 new 命令,否则会报错。Symbol 是一个原始类型的值,不是对象,所以不能添加属性,也不能被 new 操作符所作用。
4.Symbol 值不能与其他类型的值进行运算,会报错。
let 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 string5.Symbol 值可以显式转为字符串和布尔值,但是不能转为数值(会导致报错)。
let sym = Symbol('My symbol'); String(sym) // 'Symbol(My symbol)' sym.toString() // 'Symbol(My symbol)' Boolean(sym)// true6.Symbol 具有 length 属性,值为 0 。
Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。 这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
let mySymbol = Symbol(); // 第一种写法 let a = {}; a[mySymbol] = 'Hello!'; // 第二种写法 let a = { [mySymbol]: 'Hello!' }; // 第三种写法 let a = {}; Object.defineProperty(a, mySymbol, { value: 'Hello!' }); // 以上写法都得到同样结果 a[mySymbol] // "Hello!"注意:
Symbol 值作为对象属性名时,不能用点运算符。Symbol 值作为对象属性名时,必须放在方括号之中Symbol 作为属性名,该属性不会出现在for...in、for...of循环中。也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。 返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
const obj = {}; let a = Symbol('a'); let b = Symbol('b'); obj[a] = 'Hello'; obj[b] = 'World'; const objectSymbols = Object.getOwnPropertySymbols(obj); objectSymbols // [Symbol(a), Symbol(b)]Symbol.for() Symbol.for()使用此方法,可以重新使用同一个 Symbol 值
let s1 = Symbol.for('foo'); let s2 = Symbol.for('foo'); s1 === s2 // true上面的代码,表示了此方法会根据给的参数,从运行时的 symbol注册表 里找到参数对应的 symbol ,当找到了,则返回注册表中的 symbol值 ,否则会新建一个 symbol 放入全局当中
Symbol.for()与Symbol()都会生成新的 Symbol。前者会被登记在全局环境中供搜索,后者不会。
Symbol.keyFor() Symbol.keyFor()该方法用来获取返回 symbol 注册表中与某个 symbol 关联的键(参数值)。
// 创建一个 symbol 并放入 Symbol 注册表,key 为 "foo" var globalSym = Symbol.for("foo"); Symbol.keyFor(globalSym); // "foo" // 创建一个 symbol,但不放入 symbol 注册表中 var localSym = Symbol(); Symbol.keyFor(localSym); // undefined,所以是找不到 key 的更多特性和方法可以查看阮一峰老师《ECMAScript 6 入门》