持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 7 天
四种成员可见性
- public 类外可见
- private 类内可见、#var 真私有属性
- protected 子类可见
class Person {
friend?: Person // 不写就相当于 public friend?: Person
constructor(public name: string, friend?: Person) {
this.friend = friend
}
}
const p = new Person('hone')
// p,friend 可以直接出现在代码提示里面,如果去调用它,虽然是 undefined 但是不会报错
// 这个叫可见
// 可见: 你把它打出来,可以 log 它,可以对它赋值,可以读写。
class Person {
// 加了 private 在花括号外不可见, private 只能在 这个 class 里面写
private friend?: Person
constructor(public name: string, friend?: Person) {
this.friend = friend
}
xxx(){
this.friend //这里不会报错
}
} // 在这对花括号里面就可见
const p2 = new Person('jack')
// 间接的对 friend 进行赋值
const p = new Person('hone', p2)
// 但是你却读不到它
p.friend // 报错
class Person {
protected friend?: Person // 子类可见, 只有这个家族可以用
constructor(public name: string, friend?: Person) {
this.friend = friend
}
}
const p2 = new Person('jack')
const p = new Person('hone', p2)
// p.friend 不能在外面用
// 可以在 User 里面用
class User extends Person {
declare friend?: User
constructor(public id: number, name: string, friend?: User) {
super(name, friend)
}
xxx(){
this.friend
}
}
const u1 = new User(1, 'frank')
const u2 = new User(2, 'hone', u1)
u2.friend // 把 declare friend?: User 删了,这里就不可用了
class Person {
private friend?: Person
constructor(public name: string, friend?: Person) {
this.friend = friend
}
}
// 以上代码翻译成 JS 后的样子
"use strict";
class Person {
constructor(name, friend) {
this.name = name
this.friend = friend // 这里依然可以调用
}
}
如果用 TS 写了一段代码,然后别人不用 TS 引用,用 JS 去引用这个文件,那么它引用的那个代码里面就完全没有类型,虽然这个 friend 是隐藏的(private friend)、私有的,但翻译成 JS 后依然可以去调用。
那该怎么办?那就使用真正的私有属性
class Person {
#friend?: Person // 这个 friend 就是真正的私有属性
constructor(public name: string, friend?: Person) {
this.#friend = friend
}
}
// 在外面是访问不到 friend 的
// # 在 TS 里面相当于 private
static属性与static block
- 通过类名访问
- 类属性
- 静态属性
- 不能有 static name
class Person {
name: string
constructor(name: string) {
this.name = name
}
}
const p = new Person('hone')
p.name // 我们访问这个 name 得通过对象来访问
// 所以这个 name 叫做 成员属性/对象属性
// 如果有一个属性要放到 Person 上面呢?
// Person.xxx 这样是不行的
// 通过 static 把对象属性变成类的属性
class Person {
// 要在里面写
// xxx = 1 我这样写不就成了对象的属性了,那该如何?
static xxx = 1 // 那就加个 关键字,这个意思其实就是 Person.xxx = 1
name: string
constructor(name: string) {
this.name = name
}
}
Person.xxx // 这样就可以访问了
const p = new Person('hone')
不能有 static name, 会和自带属性冲突。
在 JS 里面类都是通过函数来模拟的,函数天生都有几个属性。
所以不能声明一个静态属性叫 name,它已经有了,就不要在声明了,同样也不能声明一个叫 prototype、length、arguments、caller、... 的属性会和函数自带属性冲突。
为什么需要 static 属性?
在实现某些功能的时候,有些是独有属性有些是共有属性,那么请问在 class 里面如何实现共有属性?
class Person {
// yyy: string 这种非函数不能实现共有属性,因为只要写在这里就是 这个对象独有属性
name: string
constructor(name: string) {
this.name = name
}
// 所有的函数都是对象共有的
say(){}
}
Person.xxx /
const p = new Person('hone')
// 所以让一个非函数共有 做不到
// 那怎么办?
// 通过类属性来表达原型里面的共有属性这个概念
// 共有的话就是所有对象都有同一个属性,那就把它的属性放到类上面
class Person {
static xxx = 1 // 意思就是这个 xxx 是我这个对象所有的共有属性
name: string
constructor(name: string) {
this.name = name
}
say(){}
}
Person.xxx
const p = new Person('hone')
static block
需求:我想统计一下 Foo 这个功能被用了多少次
// 每重启一次服务 class 就要初始化一遍,把这个类 class 的次数给记下来
class Foo {
static #count = 0
constructor(){
console.log(Foo.#count)
}
}
// 那么我希望你的 count 是从私有属性去读的,那么这个 let count ... 就超出了 Foo 这个类
// 用不到这个私有属性,于是 Foo.#count 报错
let count = parseInt(localStorage.getItem('count') || '0' ) // 先去看本地有没有存
count += 1
// 等到关机的时候就到 localStorage 里面去, 下次开机我在读
因为需求是要写到私有属性上去的,那怎么办?
class Foo {
static #count = 0
// 下面这个意思是这个代码在这个类创建的时候,执行的
static {
const count = loadFromLocalStorage() || 0
Foo.#count += count // 它就可以访问 Foo.#count 了
}
constructor(){
console.log(Foo.#count)
}
}
let count = parseInt(localStorage.getItem('count') || '0' )
count += 1
所以 static block 主要是用来初始化私有属性,因为我在外面初始化不了。
类和泛型
class Hash<K, V> {
map: Map<K, V> = new Map()
set(key: K, value: V) {
this.map.set(key, value)
}
get(key: K) {
return this.map.set(key)
}
}
const h = new Hash<string | number, string | number>()
h.set('name', 'hi')
h.get('name')
class Hash<K, V> extends Map<K, V> {
destroy() {
this.clear()
}
}
class 表达式
const Rectangle = class { // 可以看成一个匿名的 class
constructor(public height: number, public width: number) {
}
area(){
return this.height * this.width
}
}
const r = new Rectangle(100, 200)
抽象类(不常用)
interface A{} // 只写类型不写实现
// 有的实现有的不实现能不能用 interface ? interface 做不到有的实现
class B{} // 又写类型又写实现
// 有的实现有的不实现能不能用 class ? class 做不到有的不实现
// 如果希望实现有的实现有的不实现该如何做?
// 加关键字 abstract
// 全部都没实现
interface A {
name: string
age: number
}
// 全部都实现了
class B {
name: string
age: number
constructor(name: string, age: number){
this.name = name
this.age = age
}
}
// 折中需求:要求实现一个方法,另外一个方法只写一个类型
abstract class C {
a() {
console.log('b')
}
abstract b(): number
}
// const c = new C() 这个会报错,不能创建一个抽象类的实例,因为没有实现所以无法 new
// 也可以全部都不实现
abstract class D {
abstract name: string
age: number
constructor(age: number) {
this.age = age
}
abstract e(): number
abstract d(): number
}
// 可以做到把抽象类当接口用
// 抽象的意思是不具体,我不知道它怎么实现
abstract class Base {
abstract getName(): string
printName() {
console.log("Hello," + this.getName())
}
}
const b = new Base()
// 报错: Cannot create an instance of an abstract class.ts(2511)
abstract class C {
abstract name: string
age: number
constructor(age: number) {
this.age = age
}
abstract a(): number
abstract b(): number
}
class D extends C {
name: string
constructor(name: string) {
super(18)
this.name = name
}
a(){
return 1
}
b(){
return 2
}
}
const d = new D('hone')
把类当做参数(常用)
把类作为参数而不是把对象作为参数。
// 把 Person 作为参数怎么写?
// 以下为伪代码
class Person {}
function f(x: Person) {
const p = new x()
}
f(Person)
// new Person() 这个是对象
// Person 这个是类
class Person {
constructor(public name: string) {}
}
function f(X: typeof Person) {
const p = new X('hone')
console.log(p.name)
}
f(Person)
function f2(X: new (name: string) => Person) {
const p = new X('hone')
console.log(p.name)
}
f2(Person)
function f(X: typeof Person) { // 这里为何要这样写?
const p = new X('hone')
console.log(p.name)
}
f(Person)
// typeof Person
function ff(x: typeof 'hello') {}
ff('hello')
// 把 x: typeof 'hello' 改成 x: string
function ff(x: string) {}
ff('hello')
// 通过 ff 我们知道我们传的参数跟参数类型之间不是同等地位,类型总是比参数的具体值要高一个地位
// 比 Person 高的就是 typeof Person 这个类型
function f2(X: new (name: string) => Person) { // 前面的这个 new 的意思它是个类
const p = new X('hone')
console.log(p.name)
}
f2(Person)
// Person 的本质是一个函数
// 这个函数会接收一个 name
// (name: string) => // Person 会返回一个什么?
// 会返回一个 Person 的实例
// Person 的本质: (name: string) => p_obj
// p_obj 的类型是 Person
// (name: string) 参数照抄
// 返回值往上提一层 从 'hello' 到 string
function ff2(x: (name: string) => string ){
x('hone')
}
ff2((name) => 'hello')
class Person {
constructor(public name: string) {}
sayHi() {
console.log(`Hi, I am ${this.name}`)
}
}
function greet(constructor: new (name: string) => Person) {
const p = new constructor('hone')
p.sayHi()
}
greet(Person) // 不报错
greet(new Person('hone')) // 报错
class Person {
constructor(public name: string) {}
sayHi() {
console.log(`Hi, I am ${this.name}`)
}
}
function greet(constructor: typeof Person) {
const p = new constructor('hone')
p.sayHi()
}
greet(Person) // 不报错
greet(new Person('hone')) // 报错