🧑💼个人简介:大三学生,一个不甘平庸的平凡人🍬
🖥️ NodeJS专栏:Node.js从入门到精通
🖥️ 博主的前端之路(源创征文一等奖作品):前端之行,任重道远(来自大三学长的万字自述)
🖥️ TypeScript知识总结:TypeScript从入门到精通(十万字超详细知识点总结)
👉 你的一键三连是我更新的最大动力❤️!
文章目录
- 1、寄生组合式继承
-
- 要求
- 思路
- 代码
- 2、发布订阅模式
-
- 要求
- 思路
- 代码
- 3、观察者模式
-
- 要求
- 思路
- 代码
1、寄生组合式继承
要求
补全JavaScript
代码,要求通过寄生组合式继承使"Chinese
"构造函数继承于"Human
"构造函数。要求如下:
- 给"
Human
"构造函数的原型上添加"getName
"函数,该函数返回调用该函数对象的"name
"属性 - 给"
Chinese
"构造函数的原型上添加"getAge
"函数,该函数返回调用该函数对象的"age
"属性
思路
寄生组合式继承是引用类型最理想的继承范式,它融合了组合式继承与寄生式继承的优点,而组合式继承又是融合了原型链和借用构造函数的技术,从而发挥两者之长,所以寄生组合式继承实际是三种技术的融合。
- 寄生式继承的思路是:创建一个仅用于封装继承过程的函数
- 组合式继承的思路是:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
这一题的思路是:
-
先创建一个
inheritPrototype
函数,该函数属于寄生式继承模式,作用是实现实现对原型属性和方法的继承:// subType子类构造函数,superType父类构造函数 function inheritPrototype(subType,superType){ // Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。 var prototype = Object.create(superType.prototype); // 创建父类型的一个副本对象 prototype.constructor = subType; // 修复prototype的constructor subType.prototype = prototype; // 将prototype设为subType的原型 }
这里涉及到原型链的知识:一个构造函数的
prototype
指向它的原型对象,而它的原型对象的constructor
属性又指向到这个构造函数。上面的代码中因为要让prototype
设置为subType
的原型,所以prototype.constructor
需要指向到subType
。调用
inheritPrototype
后,subType
就继承了superType
的属性和方法,这些属性和方法存在于subType
的原型上,这样一来subType
的所有实例就能访问到同一个存在的属性或方法(这些属性和方法相当于是公有的)。 -
给"
Human
"构造函数的原型上添加"getName
"函数:Human.prototype.getName=function (){ return this.name; }
-
通过借用构造函数来实现
Chinese
对Human
实例属性的继承:function Chinese(name,age) { // 继承了Human,还传了参数 Human.call(this,name); // 借用构造函数模式 this.age = age; this.color = 'yellow'; }
在
Chinese
内部调用Human
构造函数,实际上是为Chinese
的实例设置了Human
上具有的属性和方法(不包含Human
原型上的属性和方法),这样一来Chinese
的所有实例就能拥有自己的属性和方法(这些属性和方法相当于是私有的)。 -
调用
inheritPrototype(Chinese,Human);
来实现Chinese
对Human
原型属性和方法的继承。 -
给"
Chinese
"构造函数的原型上添加"getAge
"函数:Chinese.prototype.getAge=function(){ return this.age; }
代码
function inheritPrototype(subType,superType) {
var prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function Human(name) {
this.name = name
this.kingdom = 'animal'
this.color = ['yellow', 'white', 'brown', 'black']
}
Human.prototype.getName = function () {
return this.name;
}
function Chinese(name,age) {
Human.call(this,name);
this.age = age;
this.color = 'yellow';
}
inheritPrototype(Chinese,Human);
Chinese.prototype.getAge = function() {
return this.age;
}
这题寄生组合式继承涉及到了JavaScript
面向对象的程序设计,需要理解对象,构造函数,原型,原型链等的知识,博主之后会出文章对JavaScript
面向对象的程序设计进行讲解,敬请期待!
2、发布订阅模式
要求
补全JavaScript
代码,完成"EventEmitter
"类实现发布订阅模式。
注意:
- 同一名称事件可能有多个不同的执行函数
- 通过"
on
"函数添加事件 - 通过"
emit
"函数触发事件
思路
- 因为同一名称事件可能有多个不同的执行函数,所以我们需要先定义一个
handler
对象用来保存订阅事件的列表,对象内的key
为订阅事件名称,value
是一个包含该订阅事件所有的执行函数的数组。 -
on
函数接收两个参数,分别代表订阅事件名称和执行函数,在on
函数内判断handler
对象内是否存在该订阅事件,从而决定是向handler
对象内初始化该订阅事件还是向该订阅事件的函数列表中添加新函数。 -
emit
函数接收多个参数,第一个参数代表订阅事件名称,后面的参数是需要向订阅事件处理函数传递的参数,handler
对象内存在该订阅事件时就遍历执行该订阅事件的函数列表数组中的所有处理函数。
代码
class EventEmitter {
// 补全代码
constructor() {
this.handler = {}; // 保存订阅事件的列表
}
on(type, fn) {
const fnArr = this.handler[type];
if (fnArr) {
// 如果订阅事件存在,存放订阅事件的回调函数
fnArr.push(fn);
} else {
// 如果订阅事件不存在,则初始化该事件
// 因为同一名称事件可能有多个不同的执行函数,所以用数组来存放所有的执行函数
this.handler[type] = [fn];
}
}
emit(type, ...args) {
const fnArr = this.handler[type];
if (fnArr) {
// 如果订阅事件存在,遍历并执行订阅事件的处理函数
fnArr.forEach(cb => cb(...args))
}
}
}
测试:
let sign1 = 0;
let sign2 = 0;
const emitter = new EventEmitter();
emitter.on('add', function () { sign1++ });
emitter.emit('add');
emitter.on('add', function () { sign2++ });
emitter.emit('add');
const judge = sign1 === 2 && sign2 === 1;
console.log(judge); // true
3、观察者模式
要求
补全JavaScript
代码,完成"Observer
"、"Observerd
"类实现观察者模式。
要求如下:
- 被观察者构造函数需要包含"
name
"属性和"state
"属性且"state
"初始值为"走路"。 - 被观察者创建"
setObserver
"函数用于保存观察者们。 - 被观察者创建"
setState
"函数用于设置该观察者"state
"并且通知所有观察者。 - 观察者创建"
update
"函数用于被观察者进行消息通知,该函数需要打印(console.log
)数据,数据格式为:小明正在走路。其中"小明"为被观察者的"name
"属性,"走路"为被观察者的"state
"属性。
注意:
- "
Observer
"为观察者,"Observerd
"为被观察者。
思路
- 根据题目的第二个要求:被观察者创建"
setObserver
"函数用于保存观察者们。可得知setObserver
函数应该接受一个observer
参数,该参数代表观察者,同时因为要保存这些观察则,所以在Observerd
被观察者初始化的时候应该创建一个用来保存观察者的数组observers
。 - 根据题目的第三个要求和第四个要求可知
setState
函数接受一个state
参数用来更新Observerd
被观察者自身的state
,同时setState
函数还应该遍历observers
数组,并调用数组中的每一项的update
方法,以此来通知所有观察者。
代码
// 被观察者
class Observerd {
constructor(name) {
this.name = name
this.state = '走路'
this.observers = [] // 存放观察者
}
setObserver(observer) {
this.observers.push(observer)
}
setState(state) {
this.state = state
// 遍历通知每一个观察者
this.observers.forEach(observer => {
observer.update(this)
})
}
}
// 观察者
class Observer {
// update被被观察者(Observerd)调用,用来接收被观察者的数据
update(observerd) {
console.log(observerd.name + '正在' + observerd.state);
}
}