持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 6 天
思考题:写属性时会覆盖共有属性吗?
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype = {
constructor: Person,
sayHi(target) {
console.log(`你好,${target.name}, 我是${this.name}`)
}
}
const p0 = new Person('tom', 17)
const p1 = new Person('hone', 18)
const p2 = new Person('frank', 19)
p1.sayHi = function (target) {
console.log(`${target.name} 你是个憨憨`)
}
p2.sayHi(p0) // 请问这个结果是什么?
// 结果: 你好,tom, 我是 frank
可以看到 p1 身上被加了一个 sayHi, p1 的原型上也有一个 sayHi。
可以看到 p2 的原型上有,本身是没有的。
为什么需要class
有了原型,为什么还要 class?
- 外来人口多,迫切需要 class
- class 是保留字,迟早要实现 class
- 你其实没理解原型
interface和class的区别
// 在关闭严检查后 class 和 interface 几乎一摸一样
interface PointInterface {
x: number
y: number
}
class PointClass {
x: number
y: number
}
const p = new PointClass()
p.x = 1
p.y = 1
const p2: PointInterface = {
x: 1, y: 2
}
怎么关闭严格检查? 可以在 tsconfig.json 里添加 "strictPropertyInitialization": false 或者 "strict": false
那如果都不添加,该如何解决?
有如下四种方法,可以解决
这个 ! 的意思就是告诉 TS 别检查,我会确保自己初始化的,如果没有确保自己初始化那就是 bug。
当我们写 interface 的时候,接口里面只能写类型,连默认值都给不了。
但是类它既有类型又有值,能不能只有类型没有值呢?不行。
结论
interface 只有成员的类型没有实现
class 须同时有成员的类型和实现
class之构造函数
// 第一种写法
class Point {
x: number
y: number
constructor(x=0, y=0){
this.x = x
this.y = y
}
}
const p = new Point()
console.log(p.x, p.y)
// 第二种写法
// 这个语法 JS 没有
// 通过在参数前面加上 public 就可以让它很容易的初始化
class Point {
constructor(public x: number = 0, public y: number =0) {
}
}
const p = new Point()
console.log(p.x, p.y)
以上代码意思相同
// 第三种写法
class Point {
x!: number
y!: number
// 重载,先两个假的给用户看,在写真的
constructor(x: number, y: number)
constructor(s: string)
// 这个真的保证能兼容这两个假的
constructor(xs: number | string, y?: number) {
if (typeof xs === 'number' && typeof y === 'number') {
this.x = xs
this.y = y
} else if (typeof xs === 'string') {
const parts = xs.split(',')
this.x = parseFloat(parts[0])
this.y = parseFloat(parts[1])
}
}
}
const p = new Point('1,2')
console.log(p.x, p.y)
// 第四种写法
class Hash {
[s: string]: unknown
set(key: string, value: unknown) {
this[key] = value
}
get(key: string) {
return this[key]
}
}
class可以实现接口
// 实现一个接口
interface Person {
name: string
sayHi: (target: Person) => void
}
class User implements Person {
constructor(public name: string) {
}
sayHi(target: Person) {
console.log(`Hi ${target.name}`)
}
}
// 实现两个接口
interface Person {
name: string
sayHi: (target: Person) => void
}
interface Taggable {
tags: string[]
addTag: (tag: string) => void
removeTag: (tag: string) => void
}
class User implements Person, Taggable {
constructor(public name: string){
}
tags: string[] = []
sayHi(target: Person) {
console.log(`Hi ${target.name}`)
}
addTag(tag: string) {
this.tags.push(tag)
}
removeTag(tag: string) {
// this.tags = this.tags.filter(t => t !== tag) // 这种写法比较浪费内存
const index = this.tags.indexOf(tag)
this.tags.splice(index, 1)
}
}
interface Person {
name: string
age?: number // 这一行
sayHi: (target: Person) => void
}
class User implements Person {
constructor(public name: string) {
}
sayHi(target: Person) {
console.log(`Hi ${target.name}`)
}
}
const u = new User('hone')
// 问: u.age 的值是多少?
// 1. 0
// 2. undefined
// 3. TS 报错
// 答案: 3. TS 报错
// implemets 不会帮你实现任何东西,只是添加了类型间的关联
// 那么这个 User 类 就没有实现 age
// User类 没实现为什么不会报错啊? 因为它是可选的
// 但在 User 来看,我就是没有 age
// 所以 user.age 就会报错
class能继承class
class Person {
constructor(public name: string) {}
sayHi() {
console.log(`你好,我是${this.name}`)
}
}
class User extends Person {
constructor(public id: number, name: string) {
super(name)
}
login(){}
}
const u = new User(1, 'hone')
u.name
u.id
u.sayHi()
u.login()
// 在继承的时候能不能重写 override ?
class Person {
constructor(public name: string) {}
sayHi() {
console.log(`你好,我是${this.name}`)
}
}
class User extends Person {
constructor(public id: number, name: string) {
super(name)
}
login(){}
saiHi(targe?: User) {
if (target === undefined) {
super.sayHi()
} else {
console.log(`你好,${target.name},我是${this.name]`)
}
}
}
什么是重写? 重写同名函数,这个类的方法覆盖了另一类的方法。
// 通过 declare 重写 非函数属性
class Person {
friend?: Person
constructor(public name: string, friend?: Person) {
this.friend = friend
}
}
// 有的时候我们继承了一个类,但是我们又想把某个属性的类型重新说一下
// 但是它已经有类型了,如果贸然的直接把这个类型重新写一遍会报错
// 所以得加一个关键字告诉 TS 我不是写错了,我是故意要改它的类型,把它的类型給收窄
class User extends Person {
declare friend: User // 这里也可以认为是一个断言
constructor(public id: number, name: string, friend?: User) {
super(name, friend)
}
}
const u1 = new User(1, 'frank')
const u2 = new User(2, 'hone', u1)
u2.friend