typeScript Class VS interface
<p>在面向对象(OOP)编程中,经常会使用到class(类)和interface(接口)。在TypeScript(以下简称TS)中也引入了类和接口的概念,使得TS强大的类型检测机制更加完善。就像我们所知道的,一个类是一堆抽象概念的集合,我们可以从类的构造方法中创建出享有共同属性和方法的对象。一个接口所描述的是一个对象相关的属性和方法,但并不提供具体创建此对象实例的方法。</p><p> </p>
<p>我们的前端项目使用Angular2.0+作为技术栈,Angular2.0+基于TS实现,我们在对代码中某些部分添加类型注释的时,经常出现会面对这样的问题:</p>
<blockquote>
<div class="ql-blockquote-item">"我应该使用接口还是类来对当前数据进行类型注释?"</div>
</blockquote>
<div data-header="2">Some Example</div>
<p>在开发过程中经常会有这样的操作</p>
<div class="ql-code-block-container" data-language="">
<div class="ql-code-block">fetch(<span class="hljs-string">‘https://xxx.com/api/blabla‘)</span></div>
<div class="ql-code-block">.<span class="hljs-keyword">then(<span class="hljs-keyword">data => {</span></span></div>
<div class="ql-code-block">console.<span class="hljs-built_in">log(<span class="hljs-keyword">data); // <span class="hljs-keyword">data: <span class="hljs-built_in">any</span></span></span></span></div>
<div class="ql-code-block">});</div>
<div class="ql-code-block"> </div>
</div>
<p>以上代码片段向后端API发起请求,得到返回的数据后我们会在前端UI中使用。</p>
<p>如果我们让TS编译目前这段代码,then方法中的data参数将会默认被定义为any类型。因为在没有类型定义的情况下,TS仅仅是通过分析代码,判断出类型应该是什么。</p>
<p>在这种情况下,我们为了提高程序的类型安全性,我们希望能够主动地添加一个类型注释`Response`,以便告知TS编译器我们期望当前res参数的类型为什么。</p>
<div class="ql-code-block-container" data-language="">
<div class="ql-code-block"><span class="hljs-regexp">// 需要在此处定义一个 `<span class="javascript">Response` 类型</span></span></div>
<div class="ql-code-block"> </div>
<div class="ql-code-block">fetch(<span class="hljs-string">‘https://xxx.com/api/blabla‘)</span></div>
<div class="ql-code-block">.<span class="hljs-keyword">then(<span class="hljs-function"><span class="hljs-params">(data: Response) => {</span></span></span></div>
<div class="ql-code-block"><span class="hljs-built_in">console.log(data) <span class="hljs-regexp">// data: Response</span></span></div>
<div class="ql-code-block">})</div>
<div class="ql-code-block"> </div>
</div>
<p>由此,我们引出了本篇文章的核心问题,我们应该把`Response`类型定义为interface还是一个class呢?总感觉class和interface都可以。</p>
<p> </p>
<h3 data-header="2">TS中的interface</h3>
<blockquote>
<div class="ql-blockquote-item">TS的核心原则职之一就是类型检查,关注定义的值的数据结构</div>
</blockquote>
<p>interface是仅存在于TS上下文中的一种虚拟结构,TS编译器依赖接口用于类型检查,最终编译为JS后,接口将会被移除。</p>
<p> </p>
<div class="ql-code-block-container" data-language="">
<div class="ql-code-block"><span class="hljs-selector-tag">interface <span class="hljs-selector-tag">MyInterface {</span></span></div>
<div class="ql-code-block"><span class="hljs-attribute">a: number;</span></div>
<div class="ql-code-block"><span class="hljs-attribute">b: string;</span></div>
<div class="ql-code-block">}</div>
</div>
<p>以上MyInterface这个接口约定对象只能存在且必须有两个属性,这两个属性分别为数字 `a` 和 字符串 `b`,只要不遵守此约定,TS就会抛出错误。</p>
<p> </p>
<h3 data-header="2">TS中的class</h3>
<blockquote>
<div class="ql-blockquote-item">与其他语言相比,JS并没有直接对类的描述,基于原型的继承方式也让众多的OOP世界的程序员充满困惑,一直到了ES6,class关键字作为一种语法糖出现。</div>
</blockquote>
<p>与interface不同,class作为TS的一种变量类型存在于上下文之中,class中可以提供,变量、方法等的具体实现方式等,它的作用不仅仅是约束数据结构。</p>
<div class="ql-code-block-container" data-language="">
<div class="ql-code-block"><span class="hljs-keyword">class MyClass {</span></div>
<div class="ql-code-block">a: <span class="hljs-built_in">number;</span></div>
<div class="ql-code-block">b: <span class="hljs-built_in">string;</span></div>
<div class="ql-code-block"> </div>
<div class="ql-code-block"><span class="hljs-keyword">constructor(<span class="hljs-params">options: MyInterface) {</span></span></div>
<div class="ql-code-block"><span class="hljs-keyword">this.a = options.a;</span></div>
<div class="ql-code-block"><span class="hljs-keyword">this.b = options.b;</span></div>
<div class="ql-code-block">}</div>
<div class="ql-code-block"> </div>
<div class="ql-code-block">foo(): <span class="hljs-built_in">void {</span></div>
<div class="ql-code-block"><span class="hljs-built_in">console.log(<span class="hljs-keyword">this.a);</span></span></div>
<div class="ql-code-block"><span class="hljs-built_in">console.log(<span class="hljs-keyword">this.b);</span></span></div>
<div class="ql-code-block">}</div>
<div class="ql-code-block">}</div>
</div>
<p>以上MyClass类定义了两个变量和一个foo方法,constructor方法会接受options参数初始化类中的属性。</p>
<h3 data-header="2">class和interface的比较</h3>
<p>在TS中class和interface都可以用来约束数据的结构,但是频繁使用class约束数据结构会使程序的性能受到影响,在 (https://www.tslang.cn/play/index.html) 的练习板块中,我们在左边书写TS代码,右边会显示所转换成的JS代码。</p>
<p>我们可以发现class编译了大量代码,但是interface并没有转换成任何JS,当我们定义大量的class,并且还有着复杂的继承关系时,编译过后的代码体积将更加庞大。</p>
<div data-header="2">最佳实践</div>
<p>由于考虑到class和interface在TS中编译结果的不同,我们面对不同的场景,使用正确的约束数据类型的方式,对我们代码性能层面的提高就尤为重要。</p>
<blockquote>
<div class="ql-blockquote-item">什么时候使用class</div>
</blockquote>
<p>当需要使用class时,我通常会考虑三个方面</p>
<ul data-list-style="circle">
<li class="bullet-item">是否需要创建多个实例</li>
</ul>
<ul data-list-style="circle">
<li class="bullet-item">是否需要使用继承</li>
</ul>
<ul data-list-style="circle">
<li class="bullet-item">是否需要特定的单例对象</li>
</ul>
<blockquote>
<div class="ql-blockquote-item">什么时候使用interface</div>
</blockquote>
<p>对于从服务器端获取或者业务场景中模拟的数据,提倡使用interface去定义,这些数据通常是不会经常变化和调整的,这些数据可能仅仅只表示某些状态,或者是UI上的文本。</p>
<blockquote>
<div class="ql-blockquote-item">interface配合class</div>
</blockquote>
<div class="ql-code-block-container" data-language="">
<div class="ql-code-block"><span class="hljs-keyword">class MyClass {</span></div>
<div class="ql-code-block">a: <span class="hljs-built_in">number;</span></div>
<div class="ql-code-block">b: <span class="hljs-built_in">string;</span></div>
<div class="ql-code-block">origin: MyInterface;</div>
<div class="ql-code-block"> </div>
<div class="ql-code-block"><span class="hljs-keyword">constructor(<span class="hljs-params">options: MyInterface) {</span></span></div>
<div class="ql-code-block"><span class="hljs-keyword">this.a = options.a;</span></div>
<div class="ql-code-block"><span class="hljs-keyword">this.b = options.b;</span></div>
<div class="ql-code-block"><span class="hljs-keyword">this.origin = options; <span class="hljs-comment">// 保存原始的options数据</span></span></div>
<div class="ql-code-block">}</div>
<div class="ql-code-block"> </div>
<div class="ql-code-block">foo(): <span class="hljs-built_in">void {</span></div>
<div class="ql-code-block"><span class="hljs-built_in">console.log(<span class="hljs-keyword">this.a);</span></span></div>
<div class="ql-code-block"><span class="hljs-built_in">console.log(<span class="hljs-keyword">this.b);</span></span></div>
<div class="ql-code-block">}</div>
<div class="ql-code-block">}</div>
<div class="ql-code-block"><span class="hljs-keyword">const a = <span class="hljs-keyword">new MyClass(options);</span></span></div>
<div class="ql-code-block"><span class="hljs-keyword">const b = <span class="hljs-keyword">new MyClass(a.origin);</span></span></div>
</div>
<p>在实际场景中,我们可以给class的参数指定好interface类型用来初始化class中的属性,以上代码在class的origin属性中保存了options的数据,可以用来之后初始化全新的class实例,这解决了class实例在变化后很难clone出全新实例的问题,上面实例中 `b`变量 由 `a`变量 的orgin字段初始化。</p>
<p>总之,当我们只是想要在TS中约束数据类型时,我们需要结合实际的场景去选择使用class还是interface,如果只是一个简单的后端请求,却都使用class去约束,想象一下编译出来一大堆无用的js代码,简直可怕</p>
<p>文章转自 http://www.mamicode.com/info-detail-2689875.html</p><br><br>
来源:https://www.cnblogs.com/hlandzpy/p/13213656.html
頁:
[1]