回顾 ES6 的 Class
缺少类型检查,user 里面的 name 和 age 可以随便的传值,虽然可以用 set 和 get 来进行限制;但是如果定义的一个类中,除了构造函数外 (假设有100个需要类型检查的属性值),那里面岂不是全是 get 和 set;
哪些属性是 Class 私有的、只读的,哪些是公开的呢? 虽然可以使用 es6 里面的 symbol(符号) 来定义私有的,但是有点麻烦;
代码提示不是很友好 *(vscode 有代码提示,往往都要开发者记忆类里面的关键词)*等;
// 定义一个用户的类,里面有两个属性,名字和年龄
class User {
constructor(name, age) {
this.sex = "男";
this.id = Math.random().toString(32).substr(-6);
this.name = name;
this.age = age;
}
getAge() {
return this.age;
}
static getNumber() {
return '1';
}
}
const u = new User(123, 12);
console.log(u.getAge()); // 123
console.log(User.getNumber()); // 1
TypeScript 的 Class
属性的初始化检查
在 tsconfig.json 中添加一个配置 strictPropertyInitialization: true,则属性初始化要赋值,否则会报错;
class User {
readonly id: number // 不能改变
gender: "男" | "女" = "男" // gender: "男" | "女" // 属性“gender”没有初始化表达式,且未在构造函数中明确赋值
pid?: string
constructor(public name: string, private _age: number) {
this.id = Math.random();
}
}
const u = new User("aa", 22);
属性简写
如果某个属性,通过构造函数的参数传递,并且不做任何处理的赋值给该属性,可以进行简写;
// 简写前
class User {
name: string
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
// 简写后
class User {
constructor(public name: string, public age: number) {
}
}
访问器
// 第一种:类似 Java 的形式
class User {
name: string
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
setName(value: string) {
this.name = value; // 这里可以对名字做业务处理
}
getName() {
return this.name;
}
}
const u = new User('cll', 12);
u.name = 'cll123'; // 设置值
console.log(u.name); // 获取值
// 第二种:es6+
class User {
_name: string
age: number
constructor(name: string, age: number) {
this._name = name;
this.age = age;
}
set name(value: string) {
this._name = value; // 这里可以对名字做业务处理
}
get name() {
return this._name;
}
}
const u = new User('cll', 12);
u.name = 'cll123';
console.log(u.name);
属性修饰符
readonly: 只读的,写在属性,方法等的前面;
public: 公共的,写在属性,方法等的前面,ts 默认的都是所有的属性是公开的;
protected: 受保护的,写在属性,方法等的前面,在类、子类里面可以访问,在类外部没法访问;
private: 私有的,写在属性,方法等的前面,标记改属性或者方法只能在类中使用;
class User {
readonly id: string; // 定义只读属性
name: string;
age?: number; // 定义可选属性
sex: "男" | "女" = "男"; // 初始化赋值
constructor(name: string, age: number) {
this.id = Math.random().toString(32).substr(-6);
this.name = name;
this.age = age;
}
// 定义私有的函数
private getAge(): number {
return this.age as number;
}
}
隐式实现同名接⼝
类隐式地实现了⼀个与它同名的接⼝,并且
- 该接⼝被声明为 MyClass.prototype 的类型;
- 该接⼝可以被重复声明,且⽤户声明的成员不强制要求在类声明块中实现;
interface MyClass {
c: string;
}
class MyClass {
a: string;
b: string;
}
MyClass.prototype.c = 'abc';
this 指向约束
TS 中 this 的问题
-
对象里面的 this 推断
-
类中的 this 推断
解决 TS 中 this 的问题
-
配置 noImplicitThis: true 不允许 this 隐式的指向 any,手动声明该函数中 this 的指向,解决了 对象里面的 this 问题
-
手动声明该函数中 this 的指向,将 this 作为函数的第一个参数,该参数只用于约束 this,并不是真正的参数,也不会出现在编译结果中,解决了 类中的 this 问题
案例:增加洗牌和发牌功能
// enums.ts
export enum Color {
heart = "♥",
spade = "♠",
club = "♣",
diamond = "♦"
}
export enum Mark {
A = "A",
two = "2",
three = "3",
four = "4",
five = "5",
six = "6",
seven = "7",
eight = "8",
nine = "9",
ten = "10",
eleven = "J",
twelve = "Q",
king = "K"
}
// types.ts
import { Color, Mark } from "./enums";
export interface Card {
getString(): string
}
export interface Joker extends Card {
type: "big" | "small"
}
// deck.ts
import { Card, Joker } from "./types";
import { Mark, Color } from "./enums";
interface PublishResult {
player1: Deck,
player2: Deck,
player3: Deck,
left: Deck
}
export class Deck {
private cards: Card[] = [];
constructor(cards?: Card[]) {
if (cards) {
this.cards = cards;
} else {
this.init();
}
}
private init() {
const marks = Object.values(Mark);
const colors = Object.values(Color);
for (const m of marks) {
for (const c of colors) {
this.cards.push({
color: c, mark: m,
getString() {
return this.color + this.mark;
}
} as Card);
}
}
let joker: Joker = {
type: "small",
getString() {
return "jo";
}
}
this.cards.push(joker);
joker = {
type: "big",
getString() {
return "JO"
}
}
this.cards.push(joker);
}
print() {
let result = "\n";
this.cards.forEach((card, i) => {
result += card.getString() + "\t";
if ((i + 1) % 6 === 0) {
result += "\n";
}
})
console.log(result);
}
/***
* 洗牌
*/
shuffle() {
// [x1,x2,x3,x4,x5,x6,x7]
for (let i = 0; i < this.cards.length; i++) {
const targetIndex = this.getRandom(0, this.cards.length);
const temp = this.cards[i];
this.cards[i] = this.cards[targetIndex];
this.cards[targetIndex] = temp;
}
}
//发完牌后,得到的结果有4个card[]
publish(): PublishResult {
let player1: Deck, player2: Deck, player3: Deck, left: Deck;
player1 = this.takeCards(17);
player2 = this.takeCards(17);
player3 = this.takeCards(17);
left = new Deck(this.cards);
return { player1, player2, player3, left };
}
private takeCards(n: number): Deck {
const cards: Card[] = [];
for (let i = 0; i < n; i++) {
cards.push(this.cards.shift() as Card);
}
return new Deck(cards);
}
/***
* 无法取到最大值
* @param min
* @param max
*/
private getRandom(min: number, max: number) {
const dec = max - min;
return Math.floor(Math.random() * dec + min);
}
}
// index.ts
import { Deck } from "./deck";
const deck = new Deck();
deck.shuffle();
console.log("=========洗牌之后=======");
deck.print();
const result = deck.publish();
console.log("=========发牌之后=======");
console.log("===========玩家1========");
result.player1.print();
console.log("===========玩家2========");
result.player2.print();
console.log("===========玩家3========");
result.player3.print();
console.log("===========桌面========");
result.left.print();
TypeScript👉 接口和类型兼容性
上一篇