最近在更新一个在Typescript中旅行的专栏,这是第八篇,如果对你有帮助那就帮我点个赞。
- 1、「Typescript之旅」:小红的Typescript之旅【入职】
- 2、「Typescript之旅」:小红学Typescript: 带你轻松掌握枚举🧐
- 3、一行代码引发的离奇问题,众多大佬纷纷参与点评,最终Typescript之父出手解决
- 4、「Typescript之旅」:学会satisfies操作符,让你的Typescript功力倍增!
- 5、「Typescript之旅」:看一遍就理解Ts中的class
- 6、一文盘点Typescript中23个内置类型工具! (建议收藏)
- 7、「Typescript之旅」: 彻底理解Typescript中的逆变和协变
今日更文《探索Typescript设计哲学中的主观性》。
介绍
TypeScript 是具有类型语法的 JavaScript。它基于Javascript,并且是JS的超集,但是作为一个强类型编程语言,在设计上肯定与弱类型语言在特性上会有些不同,但是ts还是基于js的,在设计上还是要侧重于js使用者们的使用习惯与ES标准、DOM标准等规范。但是不仅是这些,还包含一些Typescript的设计们的一些的“主观性”,本文会从一个小例子来引出ts的设计目标,因为我平时看ts的官网或者其设计者写的文章的时候,并没有什么很大的触动性,但是如果有例子作为佐证,心里就会想原来是这样,对为啥这样设计也会有一定的了解。
js查找元素的方式
查找元素有两个大的方式
- query系列
- getElement系列
对于第一种来说有querySelector、querySelectorAll这些方法,对于第二种有getElementById、getElementsByTagName这些。
查找方式的不同
假如html结构中有一系列的li
元素
<html>
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
</html>
想要查找li元素可以用query的方式
const ul1 = document.querySelector('ul')
const li1 = ul.querySelectorAll('li')
也可以用getElement的方式
const ul2 = document.getElementsByTagName('ul')[0]
const li2 = document.getElementsByTagName('li')
我们把这两种查找元素的方式返回的结果打印出来
查找ul
返回的是一样的,我们只看查找li
打印的结果
querySelector
方法返回的结果是 NodeList
集合, getElementsByTagName
方法返回的结果是HTMLCollection
集合。
这两种方式有啥区别?
- 在性能上,querySelector的查找性能要低于getElement的查找性能。
- querySelector返回的结果是静态的,getElement返回的结果动态的。
- 前者会指选择的元素不会随着文档操作改变,而后者会根据文档的改变自动去更新结果以反映这些变化。
第二点有点不太好理解,我们在ul中插入几条数据改变下结构看看
const ul1 = document.querySelector('ul')
const li1 = ul1.querySelectorAll('li')
for(let i =0; i<3; i++){
ul1.appendChild(document.createElement('li'))
}
console.log(li1.length); // 打印 3
li1
的集合长度是3。结果没有因为我们的改变而自动更新
const ul2 = document.getElementsByTagName('ul')[0]
const li2 = document.getElementsByTagName('li')
for(let i =0; i<3; i++){
ul2.appendChild(document.createElement('li'))
}
console.log(li2.length); // 打印 6
li1
的集合长度是6。结果根据我们的改变自动更新了。
在ts中的表现
这两种方式在ts中的表现也是不一样的。
getElementById
获取到的是HTMLElement
, querySelector
获取到的是Element
。但是我们用ts
肯定想获取到更精确的类型,这样才能有最好的体验。这二者都不太精确。因为#app
是img
标签,我们想获取的是HTMLImageElement
这个精确的类型。
在querySelector方式获取元素时,ts给我们提供了一个泛型坑位
const app1 = document.querySelector<HTMLImageElement>('#app')
通过传入的泛型坑位获取到了精确的提示。
但是getElementById却不行
所以只能用as
操作符把将app
断言为HTMLImageElement
类型。
const app = document.getElementById('app') as HTMLImageElement
// app: HTMLImageElement | null
为了找到原因,按住ctrl/command
点击到ts
的lib.dom.d.ts
类型声明文件中
可以看到查找元素的getElement
系列只有getElementsByTagName
有泛型坑位来精确其返回类型。
同样的方式查看querySelector
querySelect系列都有泛型坑位来精确返回值类型。
看到上面并没有对我们日常写业务代码有什么影响,但是有这么一种情况,当我们获取到元素后一般都会想对该元素进行一些操作,比如操作style
样式
querySelector
返回的类型是Element
,上面没有style
,合理,因为在style
这个DOM 属性是在 HTMLElement
上定义的, Element
是HTMLElement
的父级,并没有这个属性,但是在DOM标准中规定了querySelector和getElementById的返回元素类型都是Element
, 如果没有则为null, 是ts自己改了这个标准。
这是因为ts的设计者们根据过往的经验来看,getElementById
获取到的元素大多都是HTMLElement
,所以将返回类型定义为了HTMLElement
。但是DOM标准确实是规定了返回的是Element
。在ts的wiki中写道:
所以,确实没错,这就是一些主观性规则中的其中一个。
总结
在使用ts的过程中,不管是高手还是菜鸟都会遇到很多很多奇奇怪怪的问题。有很多和设计有关。有时候不仅要掌握基础的语法特性,也要了解下设计哲学,这样在看到有关ts的问题才会更加游刃有余。