С учетом следующего API:
var person = new Model({name: 'John'});
person.get('name'); //=> 'John'
person.set('name', 'Johny');
person.on('change', function(oldModel, newModel){
console.log(oldModel, newModel);
//=> {name: 'John'}, {name: 'Johny'}
});
Напишите класс модели.
Технические характеристики:
- Класс моделиимеет 3 метода: get, set и on.
- Можно зарегистрировать несколько обратных вызовов «change»
- Все зарегистрированные обратные вызовы необходимо вызывать, когда происходит изменение
- Изменениесобытие происходит всякий раз, когда новые атрибуты добавляются или изменяются с помощью метода set.
Для этого вопроса о конструкции.Я реализовал тремя различными способами ниже.Все они работают.
Мой вопрос: какой из них лучше?или почему один над другим?Или, если у вас есть лучшее решение, пожалуйста, помогите мне лучше понять концепцию вопроса.
IIFE
/** method 1: IIFE */
const IIFEModel = (function() {
function ourModel(obj) {
this.oldModel = Object.assign({}, obj);
this.newModel = obj;
}
ourModel.prototype.set = function(key, value) {
this.oldModel = Object.assign({}, this.newModel);
this.newModel[key] = value;
this.on("change");
};
ourModel.prototype.get = function(key) {
console.log(this.newModel[key]);
return this.newModel[key];
};
ourModel.prototype.on = function(eventName, handler = () => {}) {
switch (eventName) {
case "change":
return handler(this.oldModel, this.newModel);
default:
return handler();
}
};
return ourModel;
})();
Выявление образца прототипа
/** method 2: Revealing Prototype Pattern */
const PrototypeModel = function(obj) {
this.oldModel = Object.assign({}, obj);
this.newModel = obj;
};
PrototypeModel.prototype = (function() {
const set = function(key, value) {
this.oldModel = Object.assign({}, this.newModel);
this.newModel[key] = value;
this.on("change");
};
const get = function(key) {
console.log(this.newModel[key]);
return this.newModel[key];
};
const on = function(eventName, handler = () => {}) {
switch (eventName) {
case "change":
return handler(this.oldModel, this.newModel);
default:
return handler();
}
};
return {
get: get,
set: set,
on: on
};
})();
***** ОБНОВЛЕНИЕ КОДА после предложения @obfish ******
/** method 3: ES6 */
class ECMAModel {
constructor(obj) {
this.oldModel = Object.assign({}, obj);
this.newModel = obj;
this.events = new Map();
}
set(key, value) {
this.oldModel = Object.assign({}, this.newModel);
this.newModel[key] = value;
this.fire("change", this.oldModel, this.newModel);
}
get(key) {
console.log(this.newModel[key]);
return this.newModel[key];
}
on(eventName, handler) {
if (this.events.has(eventName))
this.events.set(eventName, this.events.get(eventName).concat(handler));
else this.events.set(eventName, [handler]);
}
off(eventName, handler) {
if (!this.events.has(eventName)) return;
let index = this.events.get(eventName).indexOf(handler);
if (index != -1) this.events.get(eventName).splice(index, 1);
}
fire(eventName, ...args) {
if (!this.events.has(eventName)) return;
if (!args || !args.length) args = [];
var es = this.events.get(eventName),
l = es.length;
for (var i = 0; i < l; i++) {
es[i].apply(null, args);
}
}
}
/** test case */
let person = new PrototypeModel({ name: "john" });
person.get("name");
person.set("name", "johny");
person.get("name");
person.on("change", function(oldModel, newModel) {
console.log(oldModel, newModel);
});
И я замечаю, что способ создания пользовательского события немного недостижим.Поскольку я предполагаю, что функция обратного вызова из теста имеет два элемента, которые могут иметь проблемы при рассмотрении масштабируемости.Есть ли другой способ добиться этого?