01
给类定义属性
class Point {
// x: number = 2
// y: number = 4
x: number
y: number
constructor() {
this.x = 2222
this.y = 22255
}
}
const pt = new Point()
pt.x = 33
pt.y = 22
//也可以断言
class OKG{
name!:string
}
02
class Greater {
readonly name: string = 'world'
constructor() {
this.name = 'hello'
}
}
const G = new Greater()
//G.name = 'g' //报错,无法分配到name,因为他是只读属性
//constructor() 是再new的时候运行的,可以传入参数,定义参数类型等
class Greater2 {
readonly name: string = 'world'
constructor(other?: string) {
if (other !== undefined) {
this.name = other
}
}
}
const g = new Greater2('hello')
console.log(g.name);
03
构造函数-constructor。可以定义参数,传初始值,然后再重新赋值即可
class P {
x: string;
z: number
constructor(x: string='', z: number=0) {
this.x = x;
this.z = z
}
}
const t = new P()
console.log(t.z);
console.log(t.x);
//注意:构造函数不能有类型参数,构造函数不能有返回类型注释
04
类里面的函数,我们称之为方法
class Int {
x: number;
z: number
constructor(x: number = 2, z: number = 0) {
this.x = x;
this.z = z
}
add(sum: number) {
let y = this.z + this.x
let su = sum + y
console.log(y);
console.log(su);
}
}
const pg = new Int()
pg.x = 4
pg.z = 5
pg.add(3)//传实参
05
Getters/Setters
如果存在get,但没有set属性,则改属性自动是可读的
如果没有指定setter参数的类型,它将从getter返回类型中推断出来
//访问器和设置器必须有相同的成员可见性 get和set的函数方法名相同
class C {
_length = 9;
get length() {
return this._length;
}
set length(value) {
this._length = value
}
}
const cl = new C()
console.log(cl.length);// 9
06
类
class MyClass {
[s: string]: boolean | ((s: string) => boolean)
x = true
check(s: string) {
return this[s] as boolean //断言
}
}
07
interface Pingable {
ping(): void;
}
class SO implements Pingable {
ping(): void {
console.log("ping!");
}
}
let so: SO = new SO()
so.ping()
interface A{
x:number;
y?:number
}
class C2 implements A{
x=99
}
const cll=new C2()
console.log(cll.x);//99
//console.log(cll.y);//undefined,C2上不存在属性y
08
class Animal {
dog: string
constructor(dog: string = '') {
this.dog = dog
}
}
class Dog extends Animal {
pick: string
constructor(pick: string = '') {
super()
this.pick = pick
}
}
const zoo = new Dog()
console.log(zoo.dog = 'yellow');
console.log(zoo.pick = 'pink');
09
重写方法(基类和派生类)
//基类
class Base {
greet() {
console.log('hello lady');
}
}
//派生类
class Derived extends Base {
greet(name?: string): void {
if (name === undefined) {
super.greet()
} else {
console.log(name.toUpperCase());//toUpperCase()将传入的string转换为大写
}
}
}
const d = new Derived()
d.greet()
d.greet('reader')
//注意:派生类要遵循基类的契约,契约就是,如Derived类覆盖Base类,一定要与Base兼容,比如这里传入的参数,如果name为必传参数那么就会报错,类型不匹配
const b:Base =d
b.greet()
10
基类和派生类初始化的顺序
1.基类的字段被初始化
2.基类构造函数运行
3.派生类的字段被初始化
4.派生类构造函数运行
class Basic {
name = "base";
constructor() {
console.log("my name is" + " " + this.name);
}
}
class Deri extends Basic {
name = "derived";
}
const dd = new Deri();
//优先初始化 基类的初始值
11
继承内置类型,什么是内置类型呢?比如array,error,map等等,这些TS内置的一些对象.
//例:
class MsgError extends Error{
constructor(m:string){
super(m)
// //es5及以下,可以明确地设置原型
// Object.setPrototypeOf(this,MsgError.prototype)
}
sayHello(){
return 'hello ' + this.message
}
}
const msgError=new MsgError('lady')
console.log(msgError.sayHello()); //hello lady
//这里的定义了一个类叫MsgError,它继承了Error这个内置的对象,Error就是内置的一个类或者一个对象,有时候我们在分析内置类型的结构的时候,可能出乎我们的意料,这个呢,主要表现在我们编译的时候使用的是什么target,比如我们使用ES6以上的版本,我们可能不用关心太多的关于继承内置类型的一些问题,如果是ES5及以下的版本的话,就需要注意了
12
class A{
constructor(){
}
public add(){
}
protected pro(){
}
private pri(){
}
}
const a:A=new A()
a.add()
13
类里的static区块
static 静态成员和static # 静态的区别,最大的区别是:带#号的就变成了私有的这样的属性,比如说我们可以在类的内部通过getter来去访问,但访问的时候呢,我们仍旧是可以通过类名去访问它,同时我们在类里还可以写一个static这样的块,这个static后面可以直接跟这个大括号{},然后我们可以通过类名访问我们当前类里边的通过#号来定义的这些静态的属性,那我们在类的外边试图去访问一下这个#count 是行不通的,也就是说它是私有的
class Foo {
static #count = 0;
get count() {
return Foo.#count;
}
static {
try {
const lastInstances = {
length: 100
}
Foo.#count += lastInstances.length;
}
catch{
//....
}
}
}
// Foo.#count //报错,属性#count在类FOO外部不可访问,因为它具有专用标识符
14
泛型类
class Box{
contents:Type
constructor(value:Type){
this.contents=value
}
// static defaultValue:Type//报错:静态成员不能引用类类型参数
}
const box:Box=new Box('bunny')
15
类运行时的this
class Myclass {
name = "Myclass";
// getName() {
// return this.name
// }
// //(1)箭头函数
// getName=()=> {
// return this.name
// }
//(2)this参数
getName(this:Myclass) {
return this.name
}
}
const c = new Myclass()
console.log(c.getName());//Myclass
const obj = {
name: "obj",
getName: c.getName
}
// console.log(obj.getName()); //obj
//得到的name是const下的name,而不是我们初始化指向this下的name。解决方法:(1)箭头函数。(2)this参数
//(1)使用箭头函数后得到的是Myclass
//console.log(obj.getName()); //Myclass
//(2)使用this参数后得到的是obj .就是说,谁调的我我就打印谁
console.log(obj.getName()); //obj
总结:
为了保证我们类里边的某个函数的this指向,没有问题,我们可以采用箭头函数和this参数两种方法,使用的时候一定要权衡,this这个值保证在运行的时候是正确的,即使我们没有经过TS检查代码也是如此。第二呢,使用箭头函数,它会使用更多的内存,因为每个类实例将有它自己的副本每个函数都是这样定义的,第三个呢,使用箭头函数不能在派生类中使用super.getName,也就是我们不能在子类里边通过super来调父类这个方法了,因为在原型链中没有入口可以获得基类的方法,那如果是我们使用this参数这种方式来解决这个问题,它也有一些取舍的地方,首先呢,javascripe调用者仍然可能在不知不觉的错误中来用类的方法,比方说刚才我们最后的这个调用,另外呢,每个类只有一个函数被分配,而不是每个实例会创建一个函数,另外呢,基类方法这个定义仍然可以通过super来调用,这个要优于刚才我们说的箭头函数,所以采用哪种方法取决于我们正常的业务需求即可
16
class Boxes {
contents: string = "";
set(value: string) {
this.contents = value
// console.log(this);//Boxes { contents: 'hello' }
return this;
}
}
class ClearableBoxes extends Boxes {
clear() {
this.contents = ''
}
}
const boxes = new Boxes()
const bo = boxes.set('bunny')
console.log(bo);//此时this返回的是 Boxes { contents: 'bunny' }
// boxes.set('hello')
const boxes1 = new ClearableBoxes()
const bo1 = boxes1.set('bunny')
console.log(bo1);//此时this返回的是 ClearableBoxes { contents: 'bunny' }
17
//基于类型守卫的this
//this is Type
class ValueBox{
value?: T
hasValue(): this is { value: T } {
return this.value !== undefined
}
}
const valueBox = new ValueBox()
valueBox.value = 'bunny'
if (valueBox.hasValue()) {
console.log(valueBox.value);//bunny
}
18
class Params {
constructor(public readonly x: number = 9, y: number = 99, private a: string = 'bunny') {
//...
}
}
const p = new Params()
//类表达式
const someClass = class {
content: Type
constructor(value: Type) {
this.content = value
}
}
const m = new someClass('bunny')
console.log(m.content)
19
abstract class AbstractBase{
abstract getName():string
printName(){
console.log(this.getName());
}
}
//const abst=new AbstractBase()//报错,无法创建抽象类的实例
//想要调用抽象类的函数要再声明一个类来继承抽象类
class ExtAbstract extends AbstractBase{
getName(): string {
return 'bunny'
}
}
const abstract=new ExtAbstract()
abstract.getName()
abstract.printName()
20
class Xx {
x: number = 2
y: number = 3
}
class Yy {
x: number = 24
y: number = 34
}
const xy: Xx = new Yy()
let x1 = xy.x = 88
console.log(x1);