Javascript Symbol 隐匿的未来之星
ES6中基础类型增加到了7种,比上一个版本多了一个Symbol
,貌似出现了很长时间,但却因没有使用场景,一直当作一个概念层来理解它,我想,用它的最好的方式,还是要主动的去深入了解它吧,所以我从基础部分和总结的实用场景来分析这个特性。已经了解使用方法或者时间紧迫者可以从实用场景一节开始阅读
base
首先,它给我的第一感觉就是ES6做出了很多释放语言特性方面的改变
,它能让我们更加了解语言内部机制,Symbol以对象的键值定义,比如
let key = Symbol('test');
let obj = {};
obj[key] = 'alone';
obj[key]; // "alone"
Symbol正如其名,表示一个唯一的标示,以属性的方式存在于对象当中,它接收一个参数,没有实质的作用,只是为了做一个描述。以上我们通过直接量的方式来定义它,并且取值时,也需要使用key进行读取,如果出现跨作用域的情况,是不是就不能获取了?
function sent(key){
accept({[key]:"2018"})
}
function accept(obj) {
obj[???] //我怎么拌?
}
以上两个作用域中,如果不把key传递过来,是无法读取的,一个属性还好,但是如果多了,那么靠参数传递key是不现实的. 在这种情况下,我们可以使用 Symbol.for
来为它再添加一个标示
,它接受一个参数String{key}。通常,它做为一个偏功能性的标记来表示,在全剧中它是唯一的。
function sent(key){
return accept({[key]:"2018"},key)
}
function accept(obj,key) {
console.log(Symbol.keyFor(key)) //CURRENT_YEAR
return obj[Symbol.for(Symbol.keyFor(key))] //CURRENT_YEAR
}
sent(Symbol.for('CURRENT_YEAR'))
并且使用 Symbol.for
来生成,会在存入当前全局上下文中一个<List>
结构中,我们称它为GlobalSymbolRegistry
, 顾名思义,它是全局的,所以使用key时我们需要谨慎,尤其是在大型项目中。
需要还注意以下几点:
- 读取它需要使用
getOwnPropertySymbols
方法,具体请参看MDN - Symbol() !== Symbol()
but
Symbol.for('t') === Symbol.for('t') - GlobalSymbolRegistry对象存在于当前窗口进程中,直到关闭窗口,才清除掉
目前的浏览器版本中把Symbol打印出来是字符串的格式,并没有显示具体的对象结构,我们可以直接打印 Symbol,来查看对应的prototype属性以及内部方法,所以
Symbol().__proto__ === Symbol.prototype
在使用 Symbol 做key值时,它经历了以下步骤
- 如果指向对象是没有定义的则抛出类型错误
- 如果描述符为undefined则为''
- 把描述符转换为String格式
- 生成唯一的key,并返回
- 最后一步,把这个key赋给对象,并以Symbol(des)的方式显示,其内部还是以key为准,所以 Symbol() !== Symbol() ,即便他们看起来都是 字符串的"Symbol()"
所以这样写也是可以的,但是貌似没有什么意义
var n = 1;
var key = Symbol('numer')
n[key] = ‘Symbol Number’
n[key]的时候把n隐式转换成封装对象,并为他添加Symbol,但并没有办法去通过封装对象回访这个Symbol
除了单纯的用key以外,在Symbol类下还有一些有意思的方法,following :
iterator
为指向对象添加 iterator 接口,比如使用数组解构
或者使用for of
,它接受一个generator函数
class IteratorExec {
constructor(){ this.count = 1 }
*[Symbol.iterator] = function* (){
yield this.count++;
yield this.count++;
yield this.count++;
}
}
let obj = new IteratorExec()
[...obj] //[1,2,3]
通过添加iterator
使用数据解构,还可以使用for of
let values = [];
for (let value of obj) { values.push(value) }
values; //[1,2,3]
注:ES6中Map,Set,数组和添加了Iterator
接口的对象,拥有Iterator接口.
asyncIterator
这不是ES6中的特性,貌似放到了ES7中,可以提前意淫一下如下代码:
for await (const line of readLines(filePath)) {
console.log(line);
}
toPrimitive
在对对象类型进行转换时,会进行一次 toPrimitive
,利用这个Symbol可以改变目标对象的转换规则,改变了以前的 "[object Object]"的固定形式
let obj = {
[Symbol.toPrimitive](hint){
switch(hint){
case 'number': return 5;
case 'string': return 'string';
case 'default': return 'default'
}
}
}
obj+11 // 'default11'
obj*2 // 10
这里需要注意+ Number操作是不属于 'number' 的,其他正常,这样就可以定义转对象类型的转换规则了。
toStringTag
在javascript一切皆为对象,而在每个对象中,都会有一个内部属性[[Class]]表示其对象类型,这在Symbol.toStringTag
,中是可以修改的,也就是说 '[object Object]' 后边的字符串是可自定义的
let obj = {
[Symbol.toStringTag]:'custom'
}
Object.prototype.toString(obj); // [object Object]
obj.toString(); //[object custom]
通常我们使用Object.prototype.toString读取对象属性,正是因为向后兼容,规范在对象自身的toString上实现了这种特性,而老式方法依旧使用。但是我们可以使用以下方式:
obj = {
[Symbol.toStringTag]:'custom'
get [Symbol.toStringTag](){
return 'custom'
}
}
Object.prototype.toString.call(obj)
我们把obj传入执行toString,可以达到这种效果,可以预想es6中,Object.toString是受到上下文的影响的. 显然,我们上面的两个例子都是获取的Object.prototype.toString 两者有很大区别,只有它才能准确转换
,如果你的toString不全等于它,那是无法转换的,比如
var n = new Number();
n[Symbol.toStringTag] = 123;
n.toString(); // “0”
太幼稚了,太无聊了?,Number私有的toString是直接把[[PrimitiveValue]]
转换成了字符串,这里大家要千万留心,不要误认为所有的对象添加了Symbol.toStringTag
都可以改变,如果当前对象不是纯对象,那么你可以为此对象添加一个 getter
返回对应的类型,这样外部在使用Object...call的时,会获取自定的类型。所以,这需要外部配合使用,你添加getter,人家不call你也是没办法的。
另外Symbol暴露了几种为原生对象定义了一些类型,比如
Math.toString(); //[object Math]
其他类型有 JSON, Promise, Map, TypedArray, DataView, ArrayBuffer, Genterator等等
unscopeables
const object1 = {
property1: 42
};
object1[Symbol.unscopables] = {
property1: true
};
with (object1) {
console.log(property1);
}
这个功能我感觉可用性为0,基本不用,with就是据对禁止的.
hasInstance
对于 instance
运算符,为此操作添加一个钩子,第一参数是instance的左值,我们可以返回true|false来定义运算符的返回值
var obj1 = {
[Symbol.hasInstance](instance){
return Array.isArray(Array)
}
}
class Array1 {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
[] instance obj1 //true
console.log([] instanceof Array1); //true
isConcatSpreadable
表示[].concat是否可以展开,默认是true.
let arr = [1,2];
arr.concat([3,4],5) //[1,2,3,4,5]
arr[Symbol.isConcatSpreadable] = false;
arr.concat([3,4],5) //[[1,2],3,4,5]
// 也可以把[3,4]提出来处理
let arr2 = [3,4]
arr2[Symbol.isConcatSpreadable] = false;
arr.concat(arr2,5); //[[1,2],[3,4],5]
只有在数组中这个symbol属性为false,concat操作时,就不会去解构。那么是不是意味着属性设置为ture,没有意义了?对于数组来说是的,因为它默认就是true,可是对于类数组对象,它还有一个小功能:
// (续)
arr.concat({length:2,0:3,1:4,[Symbol.isConcatSpreadable]:true}) //[1,2,3,4]
match & replace & split & search
一些字符串的操作方法,一起都说了,大概都一个意思,就是接受一个对象,然后实现一个钩子处理的函数,并返回其处理结果,它们都是可以接收正则的方法,在ES6之前,如果我们需要对字符串有比较复杂的操作基本上都是在方法外部的,必
class MyMatch {
[Symbol.match](string){return string.indexOf('world') }
}
'hello world'.match(new MyMatch()); //6
class MyReplace{
[Symbol.replace](string) {
return 'def'
}
}
'abcdef'.replace(new MyReplace(),'xxx'); //'abcxxx'
class mySplit {
[Symbol.split](val){
return val.split('-');
}
}
"123-123-123".split(new mySplit()); //['123','123','123']
class MySearch {
constructor(value) {
this.value = value;
}
[Symbol.search](string) {
return string.indexOf(this.value);
}
}
var fooSearch = 'foobar'.search(new MySearch('foo')); //0
var barSearch = 'foobar'.search(new MySearch('bar')); //3
var bazSearch = 'foobar'.search(new MySearch('baz')); //-1
practice
- 可以通过Symbol实现以上的功能性方法,比如添加 Iterator 接口,让对象队友接口特性,实际开发中,我估计很少会用到,倒是觉得
sanycIterator
是未来的前景,目前还在草案阶段 - 对于Symbol做为键值的作用,很尴尬,实际开发中,这个我也没使用过,目前为止,只需要记住它有
unique
性,比如我们想要在一个对象中添加两个一样的key名,这种需求很不常见,
var firstPerson = Symbol("peter");
var secondPerson = Symbol("peter");
var persons = {[firstPerson]:"first", [secondPerson]:"pan"};
总结
Symbol
更多的是在使用和语言本身层面暴露更多的使用方式和特性(on Object type),是的,它只以key的方式存在Object当中,在一切皆为对象中,它为 Next ECMScript Standard 提供了更多的可能性扩展性,这也是ES6中做的最大改变方面之一,虽不常用但我们还是要总结学习一下,以便在极端情况下应变自如,如果有什么文章中没有涉及到的点,欢迎补充! 注: 尤其是使用场景方面
本文转载于:猿2048https://www.mk2048.com/blog/blog.php?id=hb12k1kj1hj
Javascript Symbol 隐匿的未来之星的更多相关文章
- JavaScript Symbol对象
JavaScript Symbol对象 Symbol Symbol对象是es6中新引进的一种数据类型,它的作用非常简单,就是用于防止属性名冲突而产生. Symbol的最大特点就是值是具有唯一性,这代表 ...
- JavaScript Symbol
创建: 2019/02/26 完成: 2019/02/26 生成 每次生成的值都不一样(===, ==都是) var sym = Symbol(); // 可以有参数, 是对symbol的说明 v ...
- JavaScript 2016年的概况
国外的网站stateofjs.com根据超过九千位开发人员的问卷调查,发布了2016年JavaScript的年度概况报名. 注:本文翻译的部分可能存在不准确的情况,请以原文为准. 调查结果的报告目录结 ...
- 2016年度 JavaScript 展望(下)
[编者按]本文作者为资深 Web 开发者 TJ VanToll, TJ 专注于移动端 Web 应用及其性能,是<jQuery UI 实践> 一书的作者. 本文系 OneAPM 工程师编译呈 ...
- 【转】JavaScript 简史
本文来自众成翻译.JavaScript 毋庸置疑是当今最重要的语言之一.Web 的兴起已经把 JavaScript 带到一个前所未有的地步.下面我们来看看 JavaScript 在其短短历史中是如何演 ...
- 微服务低代码Serverless平台(星链)的应用实践
导读 星链是京东科技消金基础研发部研发的一款研发效能提升的工具平台,面向后端服务研发需求,尤其是集成性.场景化.定制化等难度不太高.但比较繁琐的需求,如服务前端的后端(BFF).服务流程编排.异步消息 ...
- JavaScript之模块化编程
前言 模块是任何大型应用程序架构中不可缺少的一部分,模块可以使我们清晰地分离和组织项目中的代码单元.在项目开发中,通过移除依赖,松耦合可以使应用程序的可维护性更强.与其他传统编程语言不同,在当前Jav ...
- 2016年度 JavaScript 展望(上)
[编者按]本文作者为资深 Web 开发者 TJ VanToll, TJ 专注于移动端 Web 应用及其性能,是<jQuery UI 实践> 一书的作者. 本文系 OneAPM 工程师编译呈 ...
- ES6:JavaScript 新特性
我相信,在ECMAScript.next到来的时候,我们现在每天都在写的JavaScript代码将会发生巨大的变化.接下来的一年将会是令JavaScript开发者们兴奋的一年,越来越多的特性提案将被最 ...
随机推荐
- Windows运行(Win+R)快速启动所有程序(自定义)
运行Win+R我们都会用,等同于开始菜单的"运行".注意,只是效果等同, 从速度来看,按win+r比用鼠标要快很多倍.用win+r启动常用程序 最常用的是输入cmd打开命令行或ca ...
- 测评 | 矩池云上架 RTX 2080 Ti 八卡机开箱
大家好,福利君今天给给大家带来的是一则消息.矩池云将上架了超微八卡GPU服务器,全新的机器组合,可靠的服务品质. 产品性能 在这里引用Lambda Labs基于FP32对多GPU扩展训练性能评测的数据 ...
- mysql中MyISAM与InooDB存储引擎的区别
MyISAM存储引擎特点 不支持事务 表级锁定 读写相互阻塞,写入不能读,读时不能写 只缓存索引 不支持外键约束 不支持聚簇索引 读取数据较快,占用资源较少 不支持MVCC(多版本并发控制机制)高并发 ...
- LGP6773题解
阴间状态,出题人是怎么想到的... 为啥lg题解全部都是直接丢状态不说是怎么想的啊.要是以后遇到阴间状态题该怎么想.jpg 首先通过观察,我们可以形象地定义染色:边权为 \(1\) 的边相当于将此边割 ...
- 网站SQL注入防御实战
SQL注入作为直接威胁web业务的最严重攻击行为,已经被大多数的网站管理员所了解,这种通过HTTP标准端口,利用网页编码不严谨,提交精心构造的代码实现对数据库非授权访问的攻击方法,已经被越来越多的sc ...
- EXCEL数据处理-经纬度转换:度分秒转换为小数
背景:工作中遇见此问题,整理了一下,花点时间随便总结下,希望能帮助到大家! 业务描述:红框内110°10′15"这种格式的经度,我想转换为110.36534这种格式. 步骤: 1.现将110 ...
- (web)Bugs_Bunny_CTF_writeup 部分简单web
Nothing here QnVnc19CdW5ueXs1MjljNDI5YWJkZTIxNzFkMGEyNTU4NDQ3MmFmODIxN30K Bugs_Bunny{529c429abde2171 ...
- Java多线程之线程同步【synchronized、Lock、volatitle】
线程同步 线程同步:当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多. ...
- C语言对Mysql函数操作
数据类型 MYSQL MYSQL结构代表一个数据库连接句柄,包含有关服务器的连接状态的信息,几乎所有函数都是用到它 typedef struct st_mysql { NET net; /* Comm ...
- 比Tensorflow还强?
大家好,我是章北海 Python是机器学习和深度学习的首选编程语言,但绝不是唯一.训练机器学习/深度学习模型并部署对外提供服务(尤其是通过浏览器)JavaScript 是一个不错的选择,市面上也出现了 ...