1、ES6为什么引入 Symbol ?
答:ES5中的对象的属性名是字符串,属性名不能重复,否则会覆盖。
当你使用别人定义的对象,并想往里面添加新的方法,
如果方法名和对象中的属性名进行重复,就会造成覆盖的后果,
因此Symbol就是为了解决这个问题而产生的。
let obj = {
[Symbol('a')]: 111,
[Symbol('a')]: 222,
}
2、Symbol类型
答:Symbol值通过Symbol()函数进行生成,可作为对象的属性名。
Symbol类型的属性名是独一无二的,不会与其他属性名产生冲突。
let s1 = Symbol(), s2 = Symbol();
s1 == s2 // false
s1 === s2 // false
3、Symbol()函数怎么使用
答:可以接受一个字符串作为参数,表示对Symbol实例的描述
因此相同参数的Symbol函数的返回值是不一样的
let s = Symbol() // Symbol()
let s1 = Symbol('s1')// Symbol(s1)
s1.toString() // 'Symbol(s1)'
4、Symbol()函数的参数如果是一个对象,会如何处理
答:会先调用对象的 toString()方法,转为字符串,然后再转为 Symbol值
var o = {a: 111}
var s = Symbol(o); // Symbol([object Object])
5、Symbol值可以和其他类型的值进行运算吗?
答:不能,会报错
6、Symbol值怎么显示的转换为字符串
答:分别使用 toString()方法或String()方法
var s = Symbol(11)
s.toString() // 'Symbol(11)'
String(s) // 'Symbol(11)'
7、Symbol值怎么显示的转换为布尔值
答:使用Boolean()方法
注意:Symbol值不能转换为数值
var s = Symbol(0)
Boolean(s) // true
8、如何获取Symbol的描述
答:通过Symbol的属性description
var s = Symbol('aa')
s.description // 'aa'
9、对象中如何设置Symbol作为对象的属性
答:有三种方式,具体如下:
let a = {}, s = Symbol()
a[s] = 'a' // 方式1
let b = {// 方式2
[s]: 222,
[Symbol()]: 111
}
let c = {}
Object.defineProperty(c, s, {value: 333}) // 方式3
10、获取对象中Symbol属性对应的值
答:使用数组获取值的方式,即:对象 [变量名] 格式
不能通过 对象.变量名 的方式进行获取
let s = Symbol()
let o = {[s]: 'Symbol值', s: 111}
o[s] // 'Symbol值'
o['s'] // 111
o.s // 111 实际上点号后面的变量名是字符串类型的
11、Symbol值可以有哪些作用
答:作为对象的属性名,保证独一无二,避免相同变量名出现的覆盖问题
定义为常量的值,保证值得独一无二
消除魔法字符串
扩展:Symbol值作为属性名,该属性是共有属性,不是私有属性
12、魔术字符串是什么
答:指的是在代码中多次出现的,与代码强耦合在一起的某一个具体字符串或者数值。
例如下面代码中switch语句中case的值 ‘child’
switch(type){
case 'child': // 'child' 为魔术字符串
return 111;
break;
}
13、怎么消除魔术字符串
答:改用变量进行代替
14、获取 对象中 Symbol类型的变量名
答:使用 Object.getOwnPropertySymbols(obj),得到以Symbol类型组成的数组
扩展:Symbol类型的属性名不能使用 for…in、 for…of 、Object.keys()、 Object.entries()、
Object.getOwnPropertyNames() 、JSON.Stringify() 遍历或者返回。
可以通过 Reflect.ownKeys()方法获取到所欲的类型的键名,包括常规键名和Symbol类型的键名
let o = {
[Symbol('a')]: 1,
[Symbol('b')]: 2
}
Object.getOwnPropertySymbols(o) // [Symbol(a), Symbol(b)]
15、如何实现重新使用同一个Symbol值
答:使用 Symbol.for(参数)
var s1 = Symbol.for(), s2 = Symbol.for(); s1 === s2; // true
var s1 = Symbol.for(1), s2 = Symbol.for(1); s1 === s2;// true
var s1 = Symbol.for('a'), s2 = Symbol.for('a'); s1 === s2;// true
16、Symbol和Symbol.for()有什么区别
答:两者都会生成新的Symbol值
Symbol.for()会被登记在全局进行搜索,不会每次调用都生成新的Symbol类型的值,
而是会检查key是否已经存在,如果不存在才会创建新的值。
Symbol()每次都是创建新的值,没有登记机制
17、Symbol.keyFor()方法
答:获取Symbol值登记的名字,无论有没有在全局环境中运行
let s1 = Symbol('a'), k1 = Symbol.keyFor(s1);
let s2 = Symbol.for('a'), k2 = Symbol.keyFor(s2);
let s3 = Symbol.for(), k3 = Symbol.keyFor(s3);
console.log(k1, k2, k3) // undefined 'a' 'undefined'
18、模块的Singleton模式
答:Singleton模式指的是调用同一个类,任何时候返回都是同一个实例。
可以通过将方法或者类或者文件挂在顶层对象的某个属性上,
由于普通属性可能有被覆盖的风险,那么可以使用Symbol类型的属性进行代替。
19、对象的Symbol.hasInstance属性作用
答:当在代码中使用 instanceof运算符,判断对象为某个对象的实例的时候,就会内部调用Symbol.hasInstance
class My{
[Symbol.hasInstance](obj){
return obj instanceof Array
}
}
[1,2,3] instanceof new My() // true
var a = {} ;
a instanceof new My() // false
20、对象的Symbol.isConcatSpreadable属性
答:当对象使用 Array.prototype.concat()方法时,是否时可展开的
数组的 Symbol.isConcatSpreadable属性值为 undefined 或者 true,进行concat拼接的时是可展开的
对象的 Symbol.isConcatSpreadable属性值为 false,进行concat拼接时不可展开,如需展开,可手动设置值为true
let obj = {length: 2, 0: 'a', 1: 'd'}
['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']
obj[Symbol.isConcatSpreadable] = true
['a', 'b'].concat(obj, 'e') // ['a', 'b', 'a', 'd', 'e']
21、对象的Symbol.species属性
答:指向当前对象的构造函数,创建实例的时候会默认调用它
class My extends Array{
static get [Symbol.species](){ return this} // 默认指向当前对象的构造函数my
}
var a = new My()
a instanceof My // true
下面代码中,修改调用构造函数的指向:
class My extends Array{
static get [Symbol.species](){ return Array} // 指向当前对象的构造函数修改为 Array
}
var a = new My()
a instanceof My // false
22、对象的Symbol.match属性
答:该属性指向一个函数,当执行 str.match(obj)时能匹配到str属性,会调用这个属性对应的方法
class My{
[Symbol.match](string){
return 'Hello world' indexOf(string)
}
}
'e'.match(new My()) // 1
23、对象的Symbol.replace属性
答:该属性指向一个方法。当对象被String.prototype.replace方法调用的时候,会调用该属性指向的方法
String.prototype.replace(searchValue, replaceValue) 等同于 searchValue[Symbol.replace](this, replaceValue)
var s = {}
s[Symbol.replace] = (...rest) => console.log(rest) // rest剩余参数,是一个数组
'hello'.replace(s, 'world') // ['hello', 'world'] ,这里Symbol.replace接收两个参数 'hello' 和 'world'
24、对象的Symbol.search属性
答:该属性指向一个方法。当对象被String.prototype.search方法调用的时候,会调用这个属性指向的方法
String.prototype.search(regexp) 等同于 regexpSymbol.search
class MySearch {
constructor(value) {
this.value = value;
}
[Symbol.search](string) {
return string.indexOf(this.value);
}
}
'foobar'.search(new MySearch('foo')) // 0
25、对象的Symbol.split属性
答:该属性指向一个方法。当对象被String.prototype.split方法调用的时候,会调用这个属性指向的方法
String.prototype.split(separator, limit) // 等同于 separator[Symbol.split](this, limit)
class MySplitter {
constructor(value) {
this.value = value;
}
[Symbol.split](string) {
let index = string.indexOf(this.value);
if (index === -1) {
return string;
}
return [
string.substr(0, index),
string.substr(index + this.value.length)
];
}
}
'foobar'.split(new MySplitter('foo'))// ['', 'bar']
26、对象的Symbol.iterator属性
答:该属性指向对象的默认遍历器方法
const myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
27、对象的Symbol.toPrimitive属性
答:该属性指向一个方法。当该对象转换为原始数据类型的值时,会调用该属性指向的方法。
该属性对应的方法有个字符串形式的入参,表示当前运算的模式。
一共有三种模式:
Number 转为数值
String 转为字符串
Default 转为数值或者字符串
let obj = {
[Symbol.toPrimitive](type){
switch(type){
case 'number': return 123;
case 'string': return 'str';
case 'default': return 'default';
default: throw new Error();
}
}
}
2 * obj // 246
3 + obj // '3default'
obj == 'default' // true
String(obj) // 'str'
28、对象的Symbol.toStringTag属性
答:该属性指向一个方法。当在对象中调用 Object.propotype.toString方法时,会调用该属性指向的方法。
这个方法中可以用来定制 ‘[object Object]’ 或者 '[object Array]'中 object后面那个字符串
({[Symbol.toStringTag]: 'Foo'}.toString()) // '[object Foo]'
class Collection {
get [Symbol.toStringTag]() {
return 'xxx';
}
}
let x = new Collection();
Object.prototype.toString.call(x) // "[object xxx]"
29、ES6 新增内置对象的Symbol.toStringTag属性值有哪些
答:共13种
JSON[Symbol.toStringTag]:'JSON'
Math[Symbol.toStringTag]:'Math'
Module 对象M[Symbol.toStringTag]:'Module'
ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'
DataView.prototype[Symbol.toStringTag]:'DataView'
Map.prototype[Symbol.toStringTag]:'Map'
Promise.prototype[Symbol.toStringTag]:'Promise'
Set.prototype[Symbol.toStringTag]:'Set'
%TypedArray%.prototype[Symbol.toStringTag]:'Uint8Array'等
WeakMap.prototype[Symbol.toStringTag]:'WeakMap'
WeakSet.prototype[Symbol.toStringTag]:'WeakSet'
%MapIteratorPrototype%[Symbol.toStringTag]:'Map Iterator'
%SetIteratorPrototype%[Symbol.toStringTag]:'Set Iterator'
%StringIteratorPrototype%[Symbol.toStringTag]:'String Iterator'
Symbol.prototype[Symbol.toStringTag]:'Symbol'
Generator.prototype[Symbol.toStringTag]:'Generator'
GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'
30、对象的Symbol.unscopables属性
答:该属性指向一个对象。当使用with关键字时,哪些属性会被with环境排除
Array.prototype[Symbol.unscopables] // 默认如下设置,有7个属性被with命令排除
/*{
// copyWithin: true,
// entries: true,
// fill: true,
// find: true,
// findIndex: true,
// includes: true,
// keys: true
}
*/
//----------------没有 unscopables 时----------------
class MyClass {
foo() { return 1; }
}
var foo = function () { return 2; };
with (MyClass.prototype) {
foo(); // 1
}
//------------------------有 unscopables 时-----------------
class MyClass {
foo() { return 1; }
get [Symbol.unscopables]() {
return { foo: true };
}
}
var foo = function () { return 2; };
with (MyClass.prototype) {
foo(); // 2
}