学习Symbol

几个基本问题

什么是Symbol

Symbol是 ES6 引入了一种新的原始数据类型,表示独一无二的值

为什么要引入(有什么作用)

为了从根本上防止属性名的冲突

Symbol( ) 函数

语法

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

Symbol 是一个原始的类似于字符串的数据类型,不是对象,没有属性名

参数部分

参数部分可以是无、字符串、对象、数值等,对symbol的转化可以转化为字符串、布尔值,但不能转化为数值

无参数

即使有两个无参数的symbol,这两个symbol也不相等

	let s1 = symbol();
	let s2 = symbol();
	s1 === s2
	//fasle
参数为对象

会调用对象的toString ,转化为字符串,再生成一个Symbol值

	const obj = {
	  toString() {
	    return 'abc';
	  }
	};
	const sym = Symbol(obj);
	sym // Symbol(abc)
Symbol 值的转化

Symbol可以显式转化为字符串

	let sym = Symbol('My symbol');
	
	String(sym) // 'Symbol(My symbol)'
	sym.toString() // 'Symbol(My symbol)'

Symbol也可以转化为布尔值

	let sym = Symbol();
	Boolean(sym) // true
	!sym  // false	

Symbol 不能转化为数值

	let sym = Symbol();
	Number(sym) // TypeError
		sym + 2 // TypeError

Symbol 的用途

作为属性名

可以利用方括号结构和**Object.defineProperty**将对象的属性名指定为一个 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 值作为对象属性名时,不能用点运算符

	const mySymbol = Symbol();
	const a = {};
	
	a.mySymbol = 'Hello!';
	a[mySymbol] // undefined ,因为点运算符后面都是字符串,mySymbol被自动认为						时字符串
	
	a['mySymbol'] // "Hello!"

在对象内定义属性的时候,Symbol 值必须放在方括号之中

	let s = Symbol();
	
	//第一种写法
	let obj = {
	  [s]: function (arg) { ... }
	};
	
	//第二种写法
	let obj = {
	  [s](arg) { ... }
	};

	obj[s](123);

定义常量

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

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

属性名的遍历

Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名

	const obj = {};
	//定义变量a、b为Symbol值
	let a = Symbol('a');   
	let b = Symbol('b');
	
	//定义对象的属性
	obj[a] = 'Hello';  
	obj[b] = 'World';
	
	const objectSymbols = Object.getOwnPropertySymbols(obj);
	
	objectSymbols
	// [Symbol(a), Symbol(b)]

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

	let obj = {
	  [Symbol('my_key')]: 1,
	  enum: 2,
	  nonEnum: 3
	};
	
	Reflect.ownKeys(obj)

	//  ["enum", "nonEnum", Symbol(my_key)]

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

其它 Symbol 方法

Symbol.for( )

用于重新使用同一个Symbol值,Symbol.for具有登记机制,每次调用返回相同的值,而Symbol( )没有登记机制,故每次调用返回不相同的值

	Symbol.for("bar") === Symbol.for("bar")
	// true
	
	Symbol("bar") === Symbol("bar")
	// false

Symbol.keyFor( )

用来返回一个已登记的 Symbol 类型值的key,未登记的显示underfined

	let s1 = Symbol.for("foo");
	Symbol.keyFor(s1) // "foo"
	
	let s2 = Symbol("foo");
	Symbol.keyFor(s2) // undefined

Symbol 属性

Symbol.hasInstance

对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。比如,foo instanceof Foo在语言内部,实际调用的是Foo [Symbol.hasInstance] (foo)

Symbol.isConcatSpreadable

对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开

	//默认数组,可以展开
	let arr1 = ['c', 'd'];
	['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
	arr1[Symbol.isConcatSpreadable] // undefined
	
	
	//当该属性设置为false时,数组不展开
	let arr2 = ['c', 'd'];
	arr2[Symbol.isConcatSpreadable] = false;
	['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']
	
	
	//类似数组的对象,默认不展开
	let obj = {length: 2, 0: 'c', 1: 'd'};
	['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']
	
	//当该属性设置为true时,展开
	obj[Symbol.isConcatSpreadable] = true;
	['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']
	
  • 数组的默认行为是可以展开,他的Symbol.isConcatSpreadable默认等于undefined。该属性等于true时,也有展开的效果
  • 类似数组的对象正好相反,默认不展开。它的Symbol.isConcatSpreadable属性设为true,才可以展开

Symbol.match

对象的Symbol.match属性,指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值

	String.prototype.match(regexp)
	// 等同于
	regexp[Symbol.match](this)

Symbol.replace

对象的Symbol.replace属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。

	String.prototype.replace(searchValue, replaceValue)
	// 等同于
	searchValue[Symbol.replace](this, replaceValue)

Symbol.search

对象的Symbol.search属性,指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值。

	String.prototype.search(regexp)
	// 等同于
	regexp[Symbol.search](this)

Symbol.split

对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。

	String.prototype.split(separator, limit)
	// 等同于
	separator[Symbol.split](this, limit)

Symbol.toStringTag

对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object]或[object Array]中object后面的那个字符串

({[Symbol.toStringTag]: 'Foo'}.toString())
// "[object Foo]"

Symbol.toPrimitive

对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值,使用三个参数来表示当前的模式

  • Number:该场合需要转成数值
  • String:该场合需要转成字符串
  • Default:该场合可以转成数值,也可以转成字符串
	let obj = {
	  [Symbol.toPrimitive](hint) {
	    switch (hint) {
	      case 'number':
	        return 123;
	      case 'string':
	        return 'str';
	      case 'default':
	        return 'default';
	      default:
	        throw new Error();
	     }
	   }
	};
	
	2 * obj // 参数为Number时,246            
	3 + obj // 参数为default时 '3default'
	obj == 'default' // 参数为default时 true
	String(obj) // 参数为string时 'str'

Symbol.iterator

对象的Symbol.iterator属性,指向该对象的默认遍历器方法

const myIterable = {};
myIterable[Symbol.iterator] = function* () { //函数生成器
  yield 1;
  yield 2;
  yield 3;
};

[...myIterable] // [1, 2, 3] ,将函数生成器里的所有制都遍历一遍 

Symbol.unscopables

对象的Symbol.unscopables属性,指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除

	Array.prototype[Symbol.unscopables]
	// {
	//   copyWithin: true,
	//   entries: true,
	//   fill: true,
	//   find: true,
	//   findIndex: true,
	//   includes: true,
	//   keys: true
	// }
	
	Object.keys(Array.prototype[Symbol.unscopables])
	// ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys']

上述的7个属性会被with命令排除

Symbol.species

对象的Symbol.species属性,指向一个构造函数。创建衍生对象时,会使用该属性

	class MyArray extends Array {
	}
	
	const a = new MyArray(1, 2, 3);
	const b = a.map(x => x);
	const c = a.filter(x => x > 1);
	
	b instanceof MyArray // true
	c instanceof MyArray // true

上面代码中,子类MyArray继承了父类Array,a是MyArray的实例,b和c是a的衍生对象,b和c都是调用数组方法生成的,实际上它们即是MyArray的实例,也是数组(Array的实例),为了使b、c直接为Array的实例,利用Symbol.species属性

	class MyArray extends Array {
	  static get [Symbol.species]() { return Array; }
	}
	
	const a = new MyArray();
	const b = a.map(x => x);
	
	b instanceof MyArray // false
	b instanceof Array // true

这样b、c就不再是MyArray的实例,而只是Array的实例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值