欢迎光临散文网 会员登陆 & 注册

【D1n910前端学习笔记】TypeScript完全解读(3/26) Symbol

2020-01-28 17:20 作者:爱交作业的D1N910  | 我要投稿

     正常操作,正常分析,大家好,我是D1n910

之前内容是


相比起来,这期内容主要讲的是ES6中的Symbol,其实可以理解为这个主要是用来调用一些js中内部方法的。

TypeScript完全解读(3/26)Symbol

typescript实现了ES6的Symbol

0 核心内容

  • 基础

  • 作为属性名

  • 属性名的遍历

  • Symbol.for 和 Symbol.keyFor

  • 11个内置Symbol值

0.1 准备工作

1、创建好symbol.ts文件,路径src/example/symbol.ts

2、之前在index.ts引入的`./example/base-type.ts`注释掉,只引入symbol

我们可以用简写

1、 基础

1.1、介绍

Symbol是ES6新增的一种基本数据类型,它和number、string、boolean、undefined和null等

是同一类型的,和object这种引入类型是有区别的。

Symbol用来表示独一无二的值,独一无二的值就代表和任何的值都不同

1.2、创建Symbol值

要创建Symbol类型的值,需要用Symbol的构建函数来生成

`const s = Symbol()`

这边我们没有使用new来创建,因为Symbol创建函数会直接返回一个Symbol值。

我们可以可以打印查看创建的s值

我们的s是一个独一无二的Symbol值,举例子,创建s1和s2后,进行对比

ts会提示报错,因为它判断到s1永远不等于s2,这个等式永远为false


咱们可以给Symbol传入参数,这边我们传入的参数是d1n910,

const s3 = Symbol('d1n910')

然后打印出出来

就会发现打印的有的d1n910,这个是作为标识使用。

如果我们再定义多一个同样标示字符串的的Symbol,

const s4 = Symbol('d1n910')

还是提示永远为false

原因就是因为我们的symbol就是独一无二的,这个标示的作用是后期打印出来让我们看到我们这个symbol是代表什么。

这个就好比地球上,有两个人,他们都可以取同一个名字,但是他们确实是两个不同的人,如果我们传入的值不是同一个字符串,而是一个数字。

那么它会在内部调用toString()方法,转换为字符串。

如果传入的是对象,那么就会报错,因为在ts中,这边的Symbol()的参数只能够传入string类型或者number类型,

当然,我们可以尝试直接在浏览器的控制台中看一下Symbol传对象会有什么样的结果

我们发现这边同样把对象运用toString方法转换成了字符串

Symbol值是不能用来做运算的

但是Symbol值可以转换为字符串和布尔类型的值

snum.toString() // 转换为字符串

snum转换为布尔值是true,因为它是一个真值

我们可以直接给它进行取反,触发隐式转换打印出false值

console.log(!snum) // false 

虽然课程里没有说到,但是这边也试一下,那就是Symbol可以作为唯一的值进行等式判断,赋值以后还是代表的同一个唯一值,这真的非常美妙。


2、Symbol作为属性名

Symbol可以作为属性名,这里我们说一下es6新的语法

你可以用变量(或者说是表达式)传参

你也可以用模版字符串来传参

这样的话就会很灵活。

我们的Symbol作为属性名来使用时,就需要用这样的方式来进行创建。

我们可以看到,它的属性名是Symbol类型的值。

使用Symbol类型的值作为属性名有一个好处,我们知道Symbol是独一无二的,当我们用Symbol作为属性名的时候,我们就能够保证我们的这个属性不会被覆盖。可以用过之前的Symbol变量去引用/修改对象的值

——当然,我们不能够用点语法去修改 XD

——我们只能用方括号去获取


3、属性名的遍历

这边用Symbol作为属性名,还有一个问题是获取对象的属性名时的遍历,目前有下面这些情况是遍历不到的。

情况一、for...in

这个会遍历出对象的属性名称,在下面d1n910Obj2的属性名称会存储在key中(我们往d1n910Obj2中多加了t1、t2属性进行观察)

打印后发现没有打印出Symbol属性名

情况二、Object.keys()

这个的方法是会返回由传入对象的属性值构建的数组,我们打印后发现返回的数组只包含t1、t2,不包含Symbol属性值

情况三、Object.geyownPropertyNames()

这个方法和Object.keys()一样,会返回由传入对象的属性值构建的数组,我们打印后发现返回的数组只包含t1、t2,不包含Symbol属性值

情况四、JSON.stringify()

这个方法会把传入的对象转换为字符串

这四种情况都是不能够获得Symbol属性名的。

但是Symbol属性在对象里不是私有属性,我们可以用

1、Object.getOwnPropertySymbols()方法

这个方法返回了一个数组,里面包含对象里所有的Symbol类型的值。

2、Reflect.ownKeys()方法

Reflect 是新语法,为操作对象而提供的新API。

Reflect.ownKeys()这个方法返回了一个数组,里面包含对象里包括Symbol类型的属性名

我们利用这个Symbol是唯一值,且只能够通过变量获取的情况,可以创建ES6类中的私有属性和私有方法的效果。我们后面讲类的时候,会再次讲到Symbol属性。

4、Symbol.for() 和 Symbol.keyFor()

4.1、Symbol.for()

这个方法和Symbol()一样,可以创建一个Symbol值,但是他们有所不同。

我们知道,Symbol()可以创建两个同样字符串标示的值,他们两个是不相同的。

const s7 = Symbol('d1n910')

const s8 = Symbol('d1n910')

console.log(s7 === s8) // false

Symbol.for()在创建时,它会这么做 

1、遍历全局变量,如果有和传入标示相同标示的Symbol值,则直接返回这个Symbol值 

2、如果没有标示相同的Symbol值,则在全局注册一个新的Symbol值。

const s9 = Symbol.for('d1n910')

const s10 = Symbol.for('d1n910')

console.log(s9 === s10) // true

* 咱们这个全局搜索,是包含了整个页面,包括Server Worker!

4.2、Symbol.keyFor()

这个方法接收一个参数,使用的是Symbol.for()方法注册的Symbol值,他会返回Symbol.for()方法全局注册的这个Symbol值的标示

* 如果不是Symbol.for创建的,则会返回undefined


5、11个内置的Symbol

很少用,但是可以了解一下

5.1、Symbol.hasInstance

这个属性指向一个内部方法,当你给一个对象设置以Symbol.hasInstance为属性名的方法后,

当其他使用instanceof来判断一个对象是否是它的实例时,就调用Symbol.hasInstance定义的方法,并且可以把调用instanceof的对象作为参数传入。

5.2、Symbol.isConcatSpreadable

 这个用来决定数组是否会被扁平化,比如我们在用concat的时候,会把数组扁平化,如果设置Symbol.isConcatSpreadable为false,则不会被扁平化。

我们可以看到后面传入的数组被扁平化了

Symbol.isConcatSpreadable是默认可读写的,所以修改之前如果我们访问,是返回undefined,如果修改后访问,则返回的是false(如上设置为false,当然,你设置为true,就是true,效果和不设置为false的效果是一样的)

5.3、Symbol.species

指定构造衍生对象的构造函数。

这边我们要涉及一些类的知识。

我们先创建一个继承Array的类 C。

因为继承,所以我们可以用原来数组定义好的方法。(这部分的内容,我们在浏览器上完成)

再通过map方法创建c1变量,map方法是遍历数组,然后返回由传入函数return值组成的数组。

我们可以尝试修改返回值看看,比如让返回值都+1

刚刚我们的c2是通过c创建的,我们管c2是c衍生的数据

我们可以用instanceof判断某个对象是不是某个类的实例。比如c是C的实例,也是C继承的类Array的实例。

c是C构造的实例,也相当于是Array的实例

由c创建出来的衍生对象c2,也是C和Array的实例。它也能够调用getName方法。


在ts中,如果这么写,我们会发现 a 是 Array构造函数的实例,但不是C构造函数的实例,这个和ES6是有区别的。

如果我们通过Symbol.species指定了衍生对象的构造函数,那么就会发现输出结果保持一致



5.4、Symbol.match

可以对字符串的match传入一个带Symbol.match方法的对象,在字符串使用match方法的时候,会调用Symbol.match指向的方法,并进行计算或者操作返回值

5.5、Symbol.replace

同上

5.6、Symbol.search

同上

5.7、Symbol.split

同上

举例子,我们写Symbol.split,改写返回的内容⬇️


5.8、 Symbol.iterator

这个属性用来获得数组的遍历器方法,通过调用这个属性对应的方法,可以返回一个遍历器对象,调用next()一次,可以获得一次当前遍历的对象,value代表当前遍历对象的值,done代表当前遍历是否结束。到了遍历结束,调用next方法返回的值是{value:undefined, done:true},done为真时,代表遍历结束。

这个Symbol.iterator是可以像上面一样改写的,所以我们可以自定义遍历器方法。

5.9、 Symbol.toPrimitive

指向一个方法,当对象被转换为原始类型值时,会调用这个方法

对象进行计算之类的情景时,可能会触发转换为原始类型的值.

* type 说明这个对象被转换为了那种原始类型的值

* 为了能够在ts中执行,所以我们这边定义了对象类型为unknown,调用会触发类型转换的number方法时,类型定义对象为number类型

执行后,我们能够在控制台发现打印了一个值`number`,是因为我们要执行自增的办法,会转换为number类型

如果我们使用转换为字符串的方法, 会打印 ‘default’

这个和js中是有区别的。

我们直接在浏览器中执行看结果是string

5.10、 Symbol.toStringTag

和Symbol.toPrimitive相似。

可以在对象中定义为字符串或者函数方法,当在对象上调用toString方法时,就会调用以这个属性名的方法。

我们把Symbol.toStringTag定义为字符串,调用toString方法,打印后看到和原本普通对象调用toString方法返回的"[object Object]"不一样了,变成了"[object d1n910]"。

我们可以改写为get方法,得到一样的结果

5.11、 Symbol.unscopables

Symbol.unscopables能够获取一个对象中你不能通过with获得的变量。

我们先看with方法是什么样的(ts中已经废弃with了,所以我们在浏览器中使用)

with方法中,可以直接调用传入对象里的属性值,而不用点语法之类的去引用。这边可能会造成全局创建变量的危险,所以现在基本上被废弃了。

Symbol.unscopables的用法是这样的,左边打印的属性代表不能够获得的变量

我们创建一个arr数组变量,发现不能调用findIndex方法,就是Symbol.unscopables查到在with环境中被过滤不能使的变量。

我们的indexOf能够调用的。


本小节完毕

推荐看阮一峰老师的ES6入门书籍。

恭喜你获得称号

了解ES6的Symbol的勇士

你的学习进度(3/26)

▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇ ▇▇

本小节仓库地址

https://github.com/D1N910/ts-learning/tree/master/03

 声明

本系列内容是D1n910通过学习Lison老师的TypeScript完全解读(26课时)整理的文字笔记,如果看完后觉得有收获,抑或想要直接支持,抑或想要直接视频原版内容,请直接到https://segmentfault.com/ls/1650000018455856?r=bPXT7X。


虽然本系列是笔记,但是也有D1n910的心血所在,请勿在未告知D1n910及获得D1n910允许的情况下直接转载本笔记——如果您是基于笔记又生成您自己的笔记,这是一点问题都没有的~

【D1n910前端学习笔记】TypeScript完全解读(3/26) Symbol的评论 (共 条)

分享到微博请遵守国家法律