[{"data":1,"prerenderedAt":412},["ShallowReactive",2],{"content:\u002F2023\u002Ftypescript\u002Fts-12":3,"surround:\u002F2023\u002Ftypescript\u002Fts-12":401},{"id":4,"title":5,"body":6,"categories":377,"date":380,"description":21,"draft":381,"extension":382,"image":383,"meta":384,"navigation":386,"path":387,"permalink":383,"published":383,"readingTime":388,"recommend":383,"references":383,"seo":393,"sitemap":394,"stem":397,"tags":398,"type":399,"updated":383,"__hash__":400},"content\u002Fposts\u002F2023\u002Ftypescript\u002Fts-12.md","面向对象之 class（上）",{"type":7,"value":8,"toc":359},"minimark",[9,14,25,29,32,35,41,44,82,86,96,102,113,119,130,136,145,149,152,158,161,167,170,176,179,185,189,195,200,203,209,212,218,221,227,230,236,240,243,249,252,258,264,270,274,280,283,303,306,312,330,333,339],[10,11,13],"h2",{"id":12},"思考题写入属性时会覆盖共有属性吗","思考题：写入属性时会覆盖共有属性吗？",[15,16,22],"pre",{"className":17,"code":19,"language":20,"meta":21},[18],"language-ts","function Person(name, age) {\n  this.name = name\n  this.age = age\n}\nPerson.prototype = {\n  constructor: Person,\n  sayHi(target) {\n    console.log(`你好，${target.name}，我是 ${this.name}`)\n  }\n}\nconst p0 = new Person(\"ClariS\", 17)\nconst p1 = new Person(\"vivy\", 18)\nconst p2 = new Person(\"k423\", 19)\np1.sayHi = function (target) {\n  console.log(`我是 ${target.name} `)\n}\np2.sayHi(p0) \n\u002F\u002F 1. 你好，ClariS，我是 k423\n\u002F\u002F 2. 我是 ClariS\n","ts","",[23,24,19],"code",{"__ignoreMap":21},[26,27,28],"p",{},"上面代码的打印结果是 1 还是 2 ？",[26,30,31],{},"答案是 1，当我们向实例对象上写入属性时，并不会覆盖原型上的共有属性",[26,33,34],{},"我们把实例对象的结构打印出来看看",[26,36,37],{},[38,39],"img",{"alt":21,"src":40},"\u002Fimages\u002Ftypescript\u002Fts-12\u002Fts_12_01.png",[26,42,43],{},"可以看到，读取与写入的规则是不一样的",[45,46,47,69],"ul",{},[48,49,50,51,54,55,54,58,61,62,65,66,68],"li",{},"读取 ",[23,52,53],{"code":53},"p0","、",[23,56,57],{"code":57},"p1",[23,59,60],{"code":60},"p2","的 ",[23,63,64],{"code":64},"sayHi"," 属性时，会先看自身上的独有属性中是否存在 ",[23,67,64],{"code":64},"，再去看原型上的共有属性；",[48,70,71,72,54,74,54,76,78,79,81],{},"而当向 ",[23,73,53],{"code":53},[23,75,57],{"code":57},[23,77,60],{"code":60}," 上写入属性时，会直接把属性写入到实例对象上，并不会覆盖原型上的 ",[23,80,64],{"code":64}," 方法。",[10,83,85],{"id":84},"interface-和-class-的区别","interface 和 class 的区别",[26,87,88,91,92,95],{},[23,89,90],{"code":90},"interface"," 和 ",[23,93,94],{"code":94},"class"," 都是用来描述对象的",[15,97,100],{"className":98,"code":99,"language":20,"meta":21},[18],"interface PointInterface {}\n\nclass PointClass {}\nconst p = new PointClass();\n",[23,101,99],{"__ignoreMap":21},[26,103,104,105,108,109,112],{},"当 ",[23,106,107],{"code":107},"tsconfig.json"," 中设置 ",[23,110,111],{"code":111},"\"strictPropertyInitialization\": false"," 时",[15,114,117],{"className":115,"code":116,"language":20,"meta":21},[18],"interface PointInterface {\n  x: number;\n  y: number;\n}\n\n\u002F\u002F 不报错\nclass PointClass {\n  x: number;\n  y: number;\n}\nconst p = new PointClass();\np.x = 1;\np.y = 1;\n",[23,118,116],{"__ignoreMap":21},[26,120,104,121,108,123,126,127,112],{},[23,122,107],{"code":107},[23,124,125],{"code":125},"\"strictPropertyInitialization\": true"," 或 ",[23,128,129],{"code":129},"\"strict\": true",[15,131,134],{"className":132,"code":133,"language":20,"meta":21},[18],"interface PointInterface {\n  x: number;\n  y: number;\n}\n\n\u002F\u002F 报错：Property 'x' has no initializer and is not definitely assigned in the constructor\nclass PointClass {\n  x: number;\n  y: number;\n}\n",[23,135,133],{"__ignoreMap":21},[26,137,138,139,141,142,144],{},"区别：",[23,140,90],{"code":90}," 只有成员的类型没有实现，",[23,143,94],{"code":94}," 须同时有成员的类型和实现",[10,146,148],{"id":147},"class-的-4-种初始化方法","class 的 4 种初始化方法",[26,150,151],{},"① 声明类型并给初始值",[15,153,156],{"className":154,"code":155,"language":20,"meta":21},[18],"class PointClass {\n  x: number = 0;\n  y: number = 0;\n}\n",[23,157,155],{"__ignoreMap":21},[26,159,160],{},"② 只给初始值，类型让 TS 自动推断",[15,162,165],{"className":163,"code":164,"language":20,"meta":21},[18],"class PointClass {\n  x = 0;\n  y = 0;\n}\n",[23,166,164],{"__ignoreMap":21},[26,168,169],{},"③ 声明类型，并在构造函数中给初始值",[15,171,174],{"className":172,"code":173,"language":20,"meta":21},[18],"class PointClass {\n  x: number;\n  y: number;\n  constructor() {\n    this.x = 0;\n    this.y = 0;\n  }\n}\n",[23,175,173],{"__ignoreMap":21},[26,177,178],{},"④ 断言，告诉 TS 别检查，我自己一定能保证初始值存在",[15,180,183],{"className":181,"code":182,"language":20,"meta":21},[18],"class PointClass {\n  x!: number;\n  y!: number;\n  constructor() {\n    this.init();\n  }\n  \u002F\u002F 当我们进行初始化操作，但初始化操作不在 constructor 中时，TS 是检测不出来的，因此需要进行断言\n  init() {\n    this.x = 0;\n    this.y = 0;\n  }\n}\n",[23,184,182],{"__ignoreMap":21},[10,186,188],{"id":187},"class-的构造函数","class 的构造函数",[15,190,193],{"className":191,"code":192,"language":20,"meta":21},[18],"class Point {\n  x: number;\n  y: number;\n  \n  constructor(x = 0, y = 0) {\n    \u002F\u002F 生成实例对象时才会执行\n    this.x = x;\n    this.y = y;\n  }\n}\nconst p = new Point()\nconsole.log(p.x, p.y)\n",[23,194,192],{"__ignoreMap":21},[196,197,199],"h3",{"id":198},"public-缩写","public 缩写",[26,201,202],{},"可以使用以下的缩写语法来代替上述写法，和上述的写法是完全等价的",[15,204,207],{"className":205,"code":206,"language":20,"meta":21},[18],"class Point {\n  constructor(public x = 0, public y = 0) {\n  }\n}\nconst p = new Point()\nconsole.log(p.x, p.y)\n",[23,208,206],{"__ignoreMap":21},[26,210,211],{},"不写初始值也不报错",[15,213,216],{"className":214,"code":215,"language":20,"meta":21},[18],"class Point {\n  constructor(public x?: number, public y?: number) {\n  }\n}\nconst p = new Point()\nconsole.log(p.x, p.y)\n",[23,217,215],{"__ignoreMap":21},[196,219,220],{"id":220},"结合函数重载",[15,222,225],{"className":223,"code":224,"language":20,"meta":21},[18],"class Point {\n  x!: number;\n  y!: number;\n\n  constructor(x: number, y: number);\n  constructor(s: string);\n\n  constructor(xs: number | string, y?: number) {\n    if (typeof xs === 'number' && typeof y === 'number') {\n      this.x = xs\n      this.y = y\n    } else if (typeof xs === 'string') {\n      const parts = xs.split(',')\n      this.x = parseFloat(parts[0])\n      this.y = parseFloat(parts[1])\n    }\n  }\n}\n\nconst p = new Point('1,2')\nconsole.log(p.x, p.y)\n",[23,226,224],{"__ignoreMap":21},[196,228,229],{"id":229},"结合索引签名",[15,231,234],{"className":232,"code":233,"language":20,"meta":21},[18],"class Hash {\n  [s: string]: unknown\n\n  set(key: string, value: unknown) {\n    this[key] = value\n  }\n  \n  get(key: string) {\n    return this[key]\n  }\n}\n",[23,235,233],{"__ignoreMap":21},[10,237,239],{"id":238},"class-实现接口","class 实现接口",[26,241,242],{},"实现一个接口",[15,244,247],{"className":245,"code":246,"language":20,"meta":21},[18],"interface Person {\n  name: string\n  sayHi: (target: Person) => void\n}\n\nclass User implements Person {\n  constructor(public name: string) {\n  }\n  sayHi(target: Person) {\n    console.log(`Hi ${target.name}`)\n  }\n}\n\u002F\u002F 通过观察不难发现：\n\u002F\u002F 类 - 类 => 继承\n\u002F\u002F 类型 - 类型 => 继承\n\u002F\u002F 类 - 类型 => 实现\n",[23,248,246],{"__ignoreMap":21},[26,250,251],{},"实现多个接口",[15,253,256],{"className":254,"code":255,"language":20,"meta":21},[18],"interface Person {\n  name: string\n  sayHi: (target: Person) => void\n}\n\ninterface Taggable {\n  tags: string[]\n  addTag: (tag: string) => void\n  removeTag: (tag: string) => void\n}\n\nclass User implements Person, Taggable {\n  tags: string[] = []\n  constructor(public name: string) {\n  }\n  sayHi(target: Person) {\n    console.log(`Hi ${target.name}`)\n  }\n  addTag(tag: string) {\n    this.tags.push(tag)\n  }\n  removeTag(tag: string) {\n    const index = this.tags.indexOf(tag)\n    this.tags.splice(index, 1)\n    \u002F\u002F this.tags = this.tags.filter(t => t !== tag)\n  }\n}\n",[23,257,255],{"__ignoreMap":21},[26,259,260,261,263],{},"即使接口中存在可选属性，也必须得在 ",[23,262,94],{"code":94}," 中实现它，不然会报错",[15,265,268],{"className":266,"code":267,"language":20,"meta":21},[18],"interface Person {\n  name: string\n  age?: number\n  sayHi: (target: Person) => void\n}\n\nclass User implements Person {\n  constructor(public name: string) {\n  }\n  sayHi(target: Person) {\n    console.log(`Hi ${target.name}`)\n  }\n}\n\nconst u = new User('ClariS')\nu.age \u002F\u002F Property 'age' does not exist on type 'User'\n",[23,269,267],{"__ignoreMap":21},[10,271,273],{"id":272},"class-继承-class","class 继承 class",[15,275,278],{"className":276,"code":277,"language":20,"meta":21},[18],"class Person {\n  constructor(public name: string) { }\n  sayHi() {\n    console.log(`你好，我是${this.name}`);\n  }\n}\n\nclass User extends Person {\n  constructor(public id: number, name: string) {\n    super(name)\n  }\n  login() { }\n}\n\nconst u = new User(1, 'ClariS');\n\nu.sayHi()\nu.login()\n",[23,279,277],{"__ignoreMap":21},[26,281,282],{},"注意：",[45,284,285,293],{},[48,286,287,288,290,291],{},"只能继承一个 ",[23,289,94],{"code":94},"，无法同时继承多个 ",[23,292,94],{"code":94},[48,294,295,296,299,300],{},"在调用 ",[23,297,298],{"code":298},"super()"," 之前不能使用 ",[23,301,302],{"code":302},"this",[196,304,305],{"id":305},"重写函数",[15,307,310],{"className":308,"code":309,"language":20,"meta":21},[18],"class Person {\n  constructor(public name: string) {}\n  sayHi() {\n    console.log(`你好，我是${this.name}`);\n  }\n}\nclass User extends Person {\n  constructor(public id: number, name: string) {\n    super(name);\n  }\n  login() {}\n  sayHi(target?: User) { \u002F\u002F target 只能是可选\n    if (target === undefined) {\n      super.sayHi();\n    } else {\n      console.log(`你好，${target.name}，我是 ${this.name}`);\n    }\n  }\n}\n",[23,311,309],{"__ignoreMap":21},[26,313,314,315,318,319,322,323,325,326,329],{},"注意函数参数类型之间的兼容关系，比如此处 ",[23,316,317],{"code":317},"Person"," 的 ",[23,320,321],{"code":321},"syaHi"," 的参数为空，那么重写的 ",[23,324,64],{"code":64}," 函数的参数 ",[23,327,328],{"code":328},"target"," 只能是可选的",[196,331,332],{"id":332},"重写属性",[15,334,337],{"className":335,"code":336,"language":20,"meta":21},[18],"class Person {\n  friend?: Person;\n  constructor(public name: string, friend?: Person) {\n    this.friend = friend;\n  }\n}\nclass User extends Person {\n  declare friend: User;\n  constructor(public id: number, name: string, friend?: User) {\n    super(name, friend);\n  }\n}\nconst u1 = new User(1, 'ClariS');\nconst u2 = new User(1, 'vivy', u1);\nu2.friend;\n",[23,338,336],{"__ignoreMap":21},[26,340,282,341,344,345,348,349,351,352,355,356,358],{},[23,342,343],{"code":343},"declare"," 后面声明 ",[23,346,347],{"code":347},"friend"," 的类型必须是兼容父类中 ",[23,350,347],{"code":347}," 的类型的，比如此处 ",[23,353,354],{"code":354},"User"," 是兼容 ",[23,357,317],{"code":317}," 的",{"title":21,"searchDepth":360,"depth":360,"links":361},4,[362,364,365,366,372,373],{"id":12,"depth":363,"text":13},2,{"id":84,"depth":363,"text":85},{"id":147,"depth":363,"text":148},{"id":187,"depth":363,"text":188,"children":367},[368,370,371],{"id":198,"depth":369,"text":199},3,{"id":220,"depth":369,"text":220},{"id":229,"depth":369,"text":229},{"id":238,"depth":363,"text":239},{"id":272,"depth":363,"text":273,"children":374},[375,376],{"id":305,"depth":369,"text":305},{"id":332,"depth":369,"text":332},[378,379],"学习笔记","TypeScript","2023-02-16",false,"md",null,{"comments":381,"slots":385},{},true,"\u002F2023\u002Ftypescript\u002Fts-12",{"text":389,"minutes":390,"time":391,"words":392},"7 min read",6.095,365700,1219,{"title":5,"description":21},{"loc":387,"images":395},[396],{"loc":40},"posts\u002F2023\u002Ftypescript\u002Fts-12",[379,94],"tech","MvIlSUkZoX0AX4Hcy7rKf-woSosyhzctph4_lAJH1Is",[402,407],{"title":403,"path":404,"stem":405,"date":406,"type":399,"children":-1},"面向对象之原型","\u002F2023\u002Ftypescript\u002Fts-11","posts\u002F2023\u002Ftypescript\u002Fts-11","2023-02-09",{"title":408,"path":409,"stem":410,"date":411,"type":399,"children":-1},"面向对象之 class（下）","\u002F2023\u002Ftypescript\u002Fts-13","posts\u002F2023\u002Ftypescript\u002Fts-13","2023-02-17",1793699839209]