原型模式
原型模式用於在建立物件時,通過共享某個物件原型的屬性和方法,從而達到提高效能、降低記憶體佔用、程式碼複用的效果。
- 示例一
function Person(name) {
this.name = name;
this.config = {
a: "1",
b: "2",
};
this.hello = function () {
console.info("hello");
};
}
假如需要通過以上程式碼建立 100 個例項,那麼將需要建立 100 個 config、100 個 hello,而這兩個東西在每個例項裡面是完全一樣的。
因此我們可以通過提取公共程式碼的方式進行油優化。
const config = {
a: "1",
b: "2",
};
const hello = function () {
console.info("hello");
};
function Person(name) {
this.name = name;
this.config = config;
this.hello = hello
}
這樣的方式使得無論建立多少個Person物件都只需要建立一個config、一個hello。 但是仍然汙染全域性變數、config被誤修改、Person和其他程式碼耦合大、不易於程式碼擴充套件維護等問題。
因此可以通過原型的方式進行優化。
function Person() {}
var p = new Person();
該函式建立例項時原型圖如下:
- 示例二
function Person(name) {
this.name = name;
this.config = {
a: "1",
b: "2",
};
this.hello = function () {
console.info("hello");
};
}
//此方式會重寫prototype,造成constructor丟失,變為Object()。
//可以使用Person.prototype.xx=yy的方式寫,或者重新指定Person.prototype.constructor=Person
Person.prototype = {
version: 1.0,
say: function (arg) {
console.info(`${this.name} say ${arg}`);
},
constructor: Person,
};
var p1 = new Person("p1");
var p2 = new Person("p2");
console.info(p1.config == p2.config); //false
console.info(p1.hello == p2.hello); //false
console.info(p1.say === p2.say); //true
p1.say("qq");
p2.say("qq");
console.info(p1.version === p2.version); //true
console.info(p1.version);
該函式建立例項時原型圖如下:
- 示例三
function Person(name) {
this.name = name;
this.config = {
a: "1",
b: "2",
};
this.hello = function () {
console.info("hello");
};
}
//此方式會重寫prototype,造成constructor丟失,變為Object()
Person.prototype = {
version: 1.0,
say: function (arg) {
console.info(`${this.name} say ${arg}`);
},
};
function PersonA(name) {
Person.call(this, name);
}
PersonA.prototype = Person.prototype;
function PersonB(name) {
Person.call(this, name);
}
PersonB.prototype = Person.prototype;
var pA = new PersonA("pa");
var pB = new PersonB("pb");
console.info(pA.config == pB.config); //false 內部屬性比較
console.info(pA.hello == pB.hello); //false 內部屬性比較
console.info(pA.say === pB.say); //true 原型方法共享
pA.say("qq");
pB.say("qq");
console.info(pA.version === pB.version); //true 原型屬性共享
console.info(pA.version); //1.0
Person.prototype.version = 2.0; //修改原型共享屬性
console.info(pB.version); //2.0
console.info(new Person().version); //2.0
//修改原型共享方法
PersonB.prototype.say = function (arg) {
console.info(`v2--- ${this.name} say ${arg}`);
};
pB.say("qq");
new Person("Person").say("ww");
-
總結
js 在建立物件比較消耗記憶體、耗時長,可以通過減少內部屬性建立的方式降低記憶體佔用。
而原型模式就是使用 javascript 語言的原型特性進行相同屬性的共享,從而達到降低記憶體佔用、提高物件建立效率。
觀察者模式
觀察者模式用於模組、元件之間通訊,通過提供統一的模式進行事件訂閱、事件釋出。從而達到模組、元件之間解耦,提高程式碼的可維護性。
模組之間、元件之間通訊方式
模組之間、元件之間採用直接引用通訊方式
const moduleA = {
say: function (msg) {
console.info("A say " + msg);
},
letBrun: function () {
//直接引用了moduleB
moduleB.run();
},
};
const moduleB = {
run: function () {
console.info("B run ");
},
letAsay: function () {
//直接引用了moduleA
moduleA.say("hello");
},
};
moduleA.letBrun(); //B Run
moduleB.letAsay(); //A say hello
模組之間、元件之間採用父元件通訊方式
const moduleA = {
say: function (msg) {
console.info("A say " + msg);
},
};
const moduleB = {
run: function () {
console.info("B run ");
},
};
const parentModule = {
moduleA,
moduleB,
letBrun: function () {
this.moduleB.run();
},
letAsay: function () {
this.moduleA.say("hello");
},
};
parentModule.letBrun(); //B Run
parentModule.letAsay(); //A say hello
事件模組實現通訊
function Emitter() {
this.events = {};
this.res_oldAction = {}
this.res_action_events = {}
}
//訂閱資源
Emitter.prototype.subscribe = function (res, action, fn) {
if(!this.res_oldAction[res.name]){
this.res_oldAction[res.name] = res[action]
res[action] = (data) => {
this.res_oldAction[res.name](data)
const fns = this.res_action_events[res.name].action;
for (let i = 0; i < fns.length; i++) {
fns[i](data);
}
}
}
if(!this.res_action_events[res.name]){
this.res_action_events[res.name] = {}
}
if(!this.res_action_events[res.name][action]){
this.res_action_events[res.name][action] = []
}
this.res_action_events[res.name].action.push(fn)
}
//取消訂閱資源
Emitter.prototype.unsubscribe = function (res, action, fn) {
const fns = this.res_action_events[res.name].action;
for (let i = 0; i < fns.length; i++) {
if (fns[i] === fn) {
fns.splice(i, 1);
i--;
}
}
}
Emitter.prototype.on = function (name, fn) {
if (!this.events[name]) {
this.events[name] = [];
}
this.events[name].push(fn);
};
Emitter.prototype.remove = function (name, fn) {
if (!this.events[name]) {
return;
}
const fns = this.events[name];
for (let i = 0; i < fns.length; i++) {
if (fns[i] === fn) {
fns.splice(i, 1);
i--;
}
}
};
Emitter.prototype.fire = function (name, data) {
if (!this.events[name]) {
return;
}
const fns = this.events[name];
for (let i = 0; i < fns.length; i++) {
fns[i](data);
}
};
const emitter = new Emitter();
//模組A中註冊事件
const methodA = (data) => {
console.info("模組A接受到food訊息:");
console.info(data);
};
emitter.on("food", methodA);
//模組B中註冊事件
const methodB = (data) => {
console.info("模組B接受到food訊息:");
console.info(data);
};
emitter.on("food", methodB);
//模組C中觸發事件
emitter.fire("food", "飯來了");
//模組B中移除事件
emitter.remove("food", methodB);
//模組C中再次觸發事件
emitter.fire("food", "飯又來了");
執行結果如下:
模組 A 接受到 food 訊息:
飯來了
模組 B 接受到 food 訊息:
飯來了
模組 A 接受到 food 訊息:
飯又來了
總結
js 元件模組的通訊方式一般分為3種(直接通訊、通過父元件通訊、通過事件模組通訊)。觀察者模式用於模組、元件之間通訊,通過提供統一的模式進行事件訂閱、事件釋出,從而達到模組、元件之間解耦,提高程式碼的可維護性