在此以前,经过《JavaScript => TypeScript 入门》已经掌握了类型声明的写法。原觉得凭着那一条无往不利的规则,就能够开开心心的重写 JS 项目了。当我跃跃欲试去重写一个 JS 项目时,发现阻碍重重。编程
在投靠 TS 时,TypeScript 容许将 JS 渐进式的过渡到 TS:segmentfault
一些简单的基本类型声明的填充,好比:number
, :string
, :string[]
等等;安全
将一些稍复杂的、暂时还未提炼好的结构声明为 any
类型;数据结构
改后缀为 .ts
, 执行 tsc
编译。编程语言
但一个明显的问题是,一个 JS 文件中每每有不少模块依赖,意味着要把全部这些模块都从新作一次类型声明,才能总体编译经过。函数
因此,要想畅通无阻的重写 JS,在上一篇入门的基础上,还得强忍着冲动,继续学习两部份内容:工具
一、TS 类的写法、特征
二、TS 模块、命名空间学习
好在,它们和 ES6 其实有着不少重合的地方——要记得,TS 是 ES6 的超集。因此若是掌握了 ES6,咱们只要挑差别,找增量,进行迁移学习,就能快速掌握上述内容。this
ES6 的类的概念,和广泛编程语言的类概念一致,表示一种特有的数据结构。它既像一个函数(能被 new
调用),又像一个对象(包含了各类属性、方法)。code
class MyClass { constructor() { this.attr = 'my attribute'; } foo(name) { console.log('hello ' + name); } } let myclass = new MyClass();
类中的方法属性被称为 “成员”,这些成员大概被分红 3 种类别:
公有属性、公有方法
私有属性、私有方法
静态属性、静态方法
在写继承时,咱们并不但愿在全部时候,这些属性、方法都被继承到子类;在访问成员时,也不但愿在任什么时候候,类实例的全部成员都无一例外能够被访问;有时候咱们但愿与此相反,这样能保持开放出去的信息简洁干净。
JS 很早就有这些概念,但到如今为止,ES6 并无处理好这个事情。
ES6 没有直观的表示私有属性、私有方法的方案,只能经过变通的方式间接地表示。
const getKeys = Symbol('getKeys_'); const attr = Symbol('attr_'); class MyClass { constructor() { // 私有属性 this[attr] = 'private attribute'; } // 公有方法 foo(config) { return this[fn](config); } // 私有方法 [getKeys](config) { return Object.keys(config); } };
这仅仅是一个间接取巧的方式避免直接被访问到,但很轻易就能绕过它:
let myclass = new MyClass(); let symbolsAttr = Object.getOwnPropertySymbols(myclass); let attr_ = myclass[symbolsAttr[0]]; // 即访问到私有属性 let symbolsFn = Object.getOwnPropertySymbols(myclass.__proto__); let getKeys_ = myclass[symbolsFn[0]]; // 即访问到私有方法
私有方法、私有属性的目的是不想开放过多的信息到外部。上面变通的方案虽然表面上达到了目的,可是不够直观,也不安全。
ES6 目前支持静态方法表示,类属性及静态属性目前做为提案还未正式成为标准。
class MyClass { name = 'jeremy'; // ES6 不被支持,仅做为提案 static version = '1.0.0'; // ES6 不被支持,仅做为提案 constructor() { console.log(MyClass.version); // '1.0.0' } static get Version() { return MyClass.version; } }
区分这些成员身份,与动态类型、静态类型语言没有必然联系,能够预见,在不久的未来,ES6 这方面将获得完善。
鉴于 TS 是 ES6 的超集这一事实,TS 类固然也有私有属性、私有方法
,静态属性、静态方法
等这些身份的成员。在标记成员身份上,TS 与 Java 有不少类似之处。好比与继承相关的访问修饰符
,以及其余限定做用的非访问修饰符
。
访问修饰符
private
public
protected
非访问修饰符
static
readonly
abstract
class User { readonly name: string; public age: number; private sex: string; protected marriage: string; constructor(name: string, sex: string) { this.name = name; this.sex = sex; } showAge() { console.log(`${this.name}, age ${this.age}`); } }
一、当成员被标记成 private
时,它就不能在声明它的类的外部访问。
二、protected
修饰符与 private
修饰符的行为很类似,但有一点不一样,protected
成员在派生类中仍然能够访问
三、被声明为 public
的类、方法、构造方法和接口可以被任何其余类访问。
class User { readonly name: string; public age: number; private sex: string; protected marriage: string; constructor(name: string, sex: string) { this.name = name; this.sex = sex; } showAge() { console.log(this.age); } } let a = new User('jerry', 'male'); console.log(a.sex); // 编译报错:私有属性不准在本类以外被访问 console.log(a.marriage); // 编译报错:私有属性不准在类以外被访问 a.name = 'jeremy'; // 编译报错:只读属性不准再次被写入
访问修饰符主要用在继承的可访问性上,继承比如遗传,用基因类比理解它们就颇有意思:
private
私有基因——本体有效,不会被继承,即子类中没法访问到
protected
被保护基因——保护血统纯正,只容许在继承体系中访问
public
公共基因——子类、其余类都能访问到
有了访问修饰符,每一个成员都有扮演着与身份对应的角色,真正作到名副其实。
访问修饰符和非访问修饰符加起来有6个甚至更多,它们如何与类、接口一块儿搭配工做,考虑到组合状况很是多,此处不去细致的探究。能够参考 Java 的规则:
default
(即缺省,什么也不写): 在同一包(等同于JS中的模块)内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
private
: 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
public
: 对全部类可见。使用对象:类、接口、变量、方法
protected
: 对同一包内的类和全部子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
固然更直观的方式是上代码 + 编译。TS 的编译工具备着很是友好、明确编译错误提示。
class Demo { static private showSelf() {} } // 编译错误提示 error TS1029: 'private' modifier must precede 'static' modifier.
固然,去查阅官方文档也是一个好办法。
非访问修饰符不关心可访问性,被它单独标记的成员,在任什么时候候都能访问到。目前至少有这些:
static
get
set
readonly
abstract
从字面上看,不难理解它们的用途。这里和 ES6 差异较大的是 abstract
。
abstract
修饰符用来定义抽象类和在抽象类内部定义抽象方法。
在 Java 中,抽象类不能用来实例化对象,主要作为其它派生类的基类使用。 不一样于接口,抽象类能够包含成员的实现细节。
TS 中也是这样规定的:抽象类不容许直接被实例化。
还有一点很重要,抽象类中的抽象方法能够不包含具体实现,但必须在派生类中实现。
// 抽象类 abstract class User { readonly name: string; public age: number; private sex: string; protected marriage: string; constructor(name: string, sex: string) { this.name = name; this.sex = sex; } showAge() { console.log(this.age); } // 抽象方法 abstract showName(): void; } // let a = new User('jerry', 'male'); // 编译报错: 抽象类不容许直接实例化
容易忽略的一个问题是,一个类中一旦出现抽象方法,那这个类整个应该被标记为 abstract
。
class UserA extends User { constructor(name: string, sex: string) { super(name, sex); // console.log(this.sex); // 编译报错:私有变量不能被访问 } // 抽象方法必需要在子类中实现 // 若无此方法,编译报错 showName() { console.log(this.name); } }
基本上,对 TS 类只须要掌握这些,深刻的部分天然在实践中继续领悟。
对了,还有模块、命名空间没有梳理。由于码文字的麻烦,只得留到后续再补。可是直接搬用 ES6 的模块import
, export
就彻底够用了。
So,这回真的去重写了。