- 为什么要有类
- 不同对象的属性反复了,就有了类
为什么要有继承
- 不同的类的属性反复了,就有了继承
- 大部分编程技巧都是为了解决反复
- 两个对象的属性反复了
let person1 = { name: "ories", age: 18, sayHi() {},};let person2 = { name: "jack", age: 23, sayHi() {},};
- 于是就有了类和构造函数
class Person { name; age; sayHi() {} constructor(name, age) { this.name = name; this.age = age; }}let person1 = new Person("ories", 18);let person2 = new Person("jack", 23);
- 咱们发现 js 的 class 特地麻烦,申明 name,写了四次,于是改写 TypeScript
class Person{ sayHi(): void {} // sayHi 的返回值为空 constructor(public name: string, public age: number){} // 因为name后面有public,所以name会变为this.name}let person1 = new Person('ories', 18)let person2 = new Person('jack', 23)
细节
- name 和 age 怎么赋值的
- 类型怎么写的
- 如何运行下面代码, ts-node
更加配置化运行 TypeScript
- 退出 tsconfig.json
- 常见配置 compilerOptions/noImplicitAny:true,禁用隐式的 any 类型
- 总结
类
- 类就是把对象的属性提前写好,防止反复
- 类外面的字段会编程对象的属性
为了节约内存,所有函数<font color=orange>都是共用的</font>(存疑 1)
- person1.sayHi === person2.sayHi
- 而<font color=orange>非函数属性是各个对象自有的</font>(存疑 2)
- 应用 <font color=skyblue>console.dir(person1)</font>能够看进去
构造函数
- 属性名尽管能够提前写好,然而属性值不行
- 所以须要构造函数接管函数,初始化属性值
- 构造函数不须要写 return,默认会 return 新对象,即 constructor 默认 return this
语法
- JS 的所有语法能够在 MDN 查看
- TS 的所有语法能够在 TS 英文/中武官网查看
- 学会触类旁通,不要妄图零碎把握
- 函数都是共用的?
- 给每个对象本身加个函数行不
class Person { mySayhi = () => {}; // 自用 sayHi(): void {} // 共用}let person1 = new Person();let person2 = new Person();console.dir(person1);console.dir(person2);
这玩意儿除了节约内存还有啥用
- React 外面十分有用
import React from "react";import ReactDOM from "react-dom";class App extends React.Component { name = "Frank"; sayHi = () => { console.log(this); console.log(`Hi, I'm ${this.name}`); }; render() { return ( <div> <button onClick={this.sayHi}>say hi</button> </div> ); }}const rootElement = document.getElementById("root");ReactDOM.render(<App />, rootElement);
- 非函数属性是各个对象自有的
- 创立一个共用的属性行吗
Person.prototype.kind = "人类";let person1 = new Person();let person2 = new Person();person1.kind === person2.kind;// 不必原型做不到(class 做不到)
- 然而能够在 class 身上加属性
class Person { static kind = "人类";}console.log(Person.kind); // 人类
- 小结
类
- 申明对象的本身属性(非函数)
- 申明对象的共有函数
- 申明对象的本身函数
- 申明类的本身属性(能够是函数)
- 继承
当初要给 Person 增加性能
- person1.on('dire',fn)
- person1.emit('die')
- person1.off('die',fn)
- 让 Person 实例具备公布订阅性能,怎么做?
- 加代码
class Person { constructor() {} sayHi() {} cache = []; on() {} off() {} emit() {}}let person1 = new Person();// 这样person1 就既是人类,有能公布订阅
- 除了人类,还有另一个类:报社
class 报社{ constructor(public name){ print(){} cache = [] on(){} off(){} emit(){} }}// 这样 报社1 就既是报社,又能公布订阅
打消反复
Person 和报社有反复属性
- 把反复属性抽出来,独自写一个类 EventEmitter
- 而后让 Person 和报社继承 EventEmitter
细节
- constructor 要调用 super()
- 以保障 EventEmitter 实例被初始化
- 继承
class EventEmitter { constructor(){} cache = [] on(){} off(){} emit(){}}class Person extends EventEmitter{ constructor(public name){ super() // super的作用是调用EventEmitter中的constructor } sayHi(){}}class 报社 extends EventEmitter{ constructor(public name){ super() } print(){}}
- 继承的其余性能
重写
- 子类重写父类的所有属性,以实现多态
- 多态的意思是不同的子类对同一个音讯有不同的反馈
class Person extends EventEmitter{ constructor(public name){ super() } sayHi(){} on(eventName, fn){ console.log('我要监听啦') super.on(eventName, fn) }}
- 继承有什么问题?
- 如果我须要更多功能怎么办?
两个抉择
- 让 EvenEmitter 继承其余类
- 让 Person 继承两个类(多继承)
- 两个办法都不好
- 组合
- 组合并没有固定的写法,只演示一种写法
- 让 Person 实现公布订阅
class Person { eventEmitter = new EventEmitter(); // 人类领有事件公布性能 name; sayHi() {} on(eventName, fn) { this.eventEmitter.on(eventName, fn); } off(eventName, fn) { this.eventEmitter.off(eventName, fn); } emit(eventName, data) { this.eventEmitter.emit(eventName, data); }}
- 优化代码
class Person { name; sayHi() {}}let person1 = new Person("frank");mixin(person1, new EventEmitter()); // 把EventEmitter的on ,off, emit 拷贝到person1function mixin(to, from) { for (let key in from) { to[key] = from[key]; } // 留神,这是最简化的mixin,理论会简单点}
- 实际上要这么写
function createEventEmitter() { const cache = []; return { on() {}, off() {}, emit() {}, };}// 组合的意思是要什么复制什么,只把地址复制过来
- 如果要更多功能
class Person { name; sayHi() {}}let person1 = new Person("ories");mixin(person1, new EventEmitter());mixin(person1, new Flyer());mixin(person1, new Killer());
- 有了组合,可能不须要 class
- 间接用函数+必包
- 举例,猫,狗,动物
dog .wangwang() .poop();cat .miaomiao() .poop();
animal // 动物父类 .poop() dog // 狗子类 .wangwang() cat // 猫子类 .miaomiao()
- 机器人
cleaningRobot .run() .clean()animal .poop() dog.wangwang() cat.miaomiao()
robot // 机器人父类 .run() murderRobot // 子类杀人机器人 .kill() cleaningRobot // 子类打扫机器人 .clean()
- 碰到需要:狗形态各杀人机器人
robot .run() murderRobot .kill() cleaningRobot .clean()animal .poop() // 这行不须要 dog .wangwang() // 这行须要,所以通过继承搞出狗型杀人机器人根本是不可能的 cat .miaomiao()
于是用组合
- dog = poop()+wangwang()
- cat = poop()+miaomiao()
- cleaningRobot = run()+clean()
- murderRobot = run()+kill()
- 狗型杀人机器人 = run()+kill()+wangwang()
- 代码怎么写, 不必 class 写 dog
const createWang = (state) => ({ wangwang: () => { console.log(`汪汪,我是${stage.name}`); },});const createRun = (state) => ({ run: () => { state.position += 1; },});const createDog = (name) => { const state = { name, position: 0 }; // 这个state是一个闭包 return Object.assign({}, createWang(state), createRun(state)); // 空对象最初就剩下wangwang,run的地址, // {}是惟一裸露的进口 // 对象是富人的必包,java没有闭包只能用对象封装性能 // 如果有闭包,就用对象,闭包,对象裸露api,api去操作这个对象};const dog = createDog("小白");
- 最早写前端的一批人由 java 转入,他们带来了 class 继承,多态
- 前端写着写着发现代码越写越简单
- 后端有些数据结构绝对固定,然而事实上前端的需要很灵便,
- 这样子前端就开始想是不是面向对象的问题,继承为什么难用,一旦呈现穿插就搞不过去
- 所以往其余方向摸索,一大分支就是函数式编程,以上就是函数来搞定
- 狗型杀人机器人怎么写
const createMurderRobotDog = (name) => { const state = { name, position: 0 }; return Object.assign( {}, createWang(state), createRun(state), createKill(stage) );};const murderRobotDog = createMurderRobotDog("小黑");
- 组合的总结
- 组合的毛病,写法太灵便
- 组合优于继承
- 从新再写一遍组合
- 用 wangwang 和 poop 发明出 dog
// dog = poop + wangwangconst createPoop = (state) => ({ // 这里不加括号会有bug,js会认为是代码块,不返回对象 poop() { state.weight -= 1; console.log(`我在拉屎,体重变为${state.weight}`); },});p = createPoop({ weight: 100 });p.poop();const createWang = (state) => ({ wangwang() { console.log(`汪汪,我叫${state.name}`); },});w = createWang({ name: "小白" });const createDog = (name) => { const state = { name: name, weight: 100 }; return Object.assign({}, createWang(state), createPoop(state));};dog = createDog("小白");dog.wangwang();dog.poop();
- 什么时候用继承
场景
- 开发者不会用组合
- 性能没有太多穿插的中央,一眼就能看出继承关系
- 前端库比拟喜爱用继承
举例
- const vm = new Vue({...})
// 共有属性上有on emit off// 为代码其就是mixin(Vue.prototype, eventemitter) // 组合
- Vue 混入了 EventEmitter
- class App extends React.Component{...}
- React.Component 内置了不便的复用代码(钩子)
- 什么时候用组合
场景
- 性能组合非常灵活,无奈一眼看出继承关系
- 插件零碎,Vue 的插件就是将性能复制到 Vue 的原型
// Vue的源代码// Vue.use 承受一个插件,或者对象有一句installedPlugins.push(plugin) // 把插件装置到被装置的插件里// 详情见Vue文档开发插件3,把created复制到以后实例// 或者4.在Vue.prototype去增加属性
- mixin 模式
源代码中的mergeOptions,其实就是复制属性到Vue的options
举例
- Vue.mixin()
- Vue.use(MyPlugin)
- React 接管组件的组件,因为 react 用函数,组件组合起来更不便
function App() { return <div className="App">{connect(A, B)}</div>;}function A(props) { return <div>我是A,我的儿子是{props.children}</div>;}function B(props) { return <div>我是B</div>;}function connect(Component1, component2) { return ( <Fragment> <Component1></Component1> | <Component2 /> </Fragment> );}
- 总结
- 如果开发根底库,用继承
- 业务开发应用组合
- 不肯定对,须要灵便抉择
- 组合更占内存么?
- 通过将函数写在里面解决,会产生新的函数
const f1 = () => {};createF1 = (state) => ({ f1: f1 });const f2 = () => {};createF2 = (state) => ({ f2: f2 });createDog = (name) => { const state = { name }; return Object.assign({}, createF1(), createF2());};dog1 = createDog(1);dog2 = createDog(2);dog1.f1 === dog2.f1;
- 然而面向对象也会产生先的对象
- 在原理上没太多区别
- 感觉对你有帮忙的敌人,请关注公众号!
本文由博客一文多发平台 OpenWrite 公布!