JavaScriptコンポーネント設計思想(二)
13064 ワード
2016年3月に「JavaScriptコンポーネント設計思想」という文章を書きましたが、コンポーネント化の実現方法と各コンポーネントの結合度を低減する説明があります.その中でも「事件のメカニズム」は良い選択です.もっと多くの実践を通して、私にもっと多くの思考をもたらしました.
イベントの実現には、日常開発において、私たちは常にこのようなイベントを必要としています.
一、伝統的なイベント方式
共通の“公共”の事件、実現は簡単で、多くのモジュールは同時に事件を備える時比較的に維持しにくいです!
Personはイベントの一種ですか?関係が複雑で、曖昧さが生じやすく、理解しにくいです.
mixin(des,src)は二つのパラメータを受け取ります.関数の目的は、プロバイダが持つエニュメレート・プロパティーを受信者にコピーすることです.
上記で述べた「伝統的な方式」、「疑似的な継承」、「混入方式」の3つがプロジェクトの中で実現されていますが、どうやって選択すればいいですか?個人的には、プロジェクトでは、少なくとも2つのイベントが必要です.「バスイベント」と「支線イベント」.バスイベント:各モジュール間のリリース、購読(グローバル)を処理します.サブイベント:具体的なモジュール内部のリリース、購読(ローカル)!
イベントの実現には、日常開発において、私たちは常にこのようなイベントを必要としています.
function Event() {
this._events = Object.create(null); //
}
Event.prototype = {
constructor: Event,
// key: ,listener: ( )
on: function (event, fn) {
var eventTarget = this;
(eventTarget._events[event] || (eventTarget._events[event] = [])).push(fn);
return eventTarget;
},
//
once: function (event, fn) {
var eventTarget = this;
function on() {
eventTarget.off(event, on);
fn.apply(eventTarget, arguments);
}
on.fn = fn;
eventTarget.on(event, on);
return eventTarget
},
//
off: function (event, fn) {
var eventTarget = this;
//
if (!arguments.length) {
eventTarget._events = Object.create(null);
return eventTarget
}
var cbs = eventTarget._events[event];
if (!cbs) {
return eventTarget
}
//
if (arguments.length === 1) {
eventTarget._events[event] = null;
return eventTarget
}
//
var cb;
var i = cbs.length;
while (i--) {
cb = cbs[i];
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1);
break
}
}
return eventTarget
},
//
emit: function (event) {
var eventTarget = this;
var cbs = eventTarget._events[event];
if (cbs) {
cbs = cbs.length > 1 ? Array.prototype.slice.call(cbs) : cbs;
var args = Array.prototype.slice.call(arguments, 1);
for (var i = 0, l = cbs.length; i < l; i++) {
cbs[i].apply(eventTarget, args);
}
}
return eventTarget
}
};
開発において、ユーザー登録が成功したら、私たちは「header」、「toolbar」、「menu」を初期化する必要があります.通常のやり方は登録成功のコールバックで対応モジュールの初期化関数を呼び出すことですが、このように各モジュール間の結合度が高すぎて、開発仕様に合わないです.だから、よく事件を借りて実現します.しかし、ある状態を「toolbar」で修正しました.他のモジュールにも対応した操作をするように通知します.この時には、「toolbar」にも事件行動を持たせる必要があります.今はPersonクラスを持っています.イベント行動を備えてほしいです.下記はいくつかの実施形態である.一、伝統的なイベント方式
共通の“公共”の事件、実現は簡単で、多くのモジュールは同時に事件を備える時比較的に維持しにくいです!
// Event
var event = new Event();
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log(this.name);
// (Event )
event.emit("Person.sayName", this,name);
};
// (Event )
event.on("Person.sayName", function(args){
console.log(args.name + " emit Person.sayName event!");
});
var p = new Person("ligang");
p.sayName();
二、疑似類の継承方式Personはイベントの一種ですか?関係が複雑で、曖昧さが生じやすく、理解しにくいです.
//
Person.prototype = new Event(); // Object.create(Event.prototype);
Person.prototype.constructor = Person;
Person.prototype.sayName = function(){
console.log(this.name);
// ( Person )
this.emit("Person.sayName", this,name);
};
var p = new Person("ligang");
// ( Person )
p.on("Person.sayName", function(args){
console.log(args.name + " emit Person.sayName event!");
});
p.sayName();
// person Event
console.log(p instanceof Person); // true
console.log(p instanceof Event); // true
三、混入方式mixin(des,src)は二つのパラメータを受け取ります.関数の目的は、プロバイダが持つエニュメレート・プロパティーを受信者にコピーすることです.
function mixin(des, src){
for(var prop in src){
if(src.hasOwnProperty(prop)){
des[prop] = src[prop];
}
}
return des;
}
ES 6に追加された方法Object.assign()
は、オブジェクトのマージのために、ソースオブジェクトのエニュメレート・属性のすべてを対象オブジェクトにコピーする同じ目的を達成することができる.enumerableがfalseの属性であることを無視して、オブジェクト自体のエミュレート可能な属性のみをコピーします.なお、mixinとObject.assignは、深くコピーするのではなく、浅いコピーを実行しています.つまり、ソースオブジェクトのある属性の値が対象であれば、対象のオブジェクトがコピーされるのはこのオブジェクトの参照です.var src = {a: {b: 1}, c: 1};
var des = {};
Object.assign(des, src);
src.a.b = 2;
src.c = 2;
console.log(des.a.b); // 2
console.log(des.c); // 1
混入方式は考え方がはっきりしていますが、Event構造関数を修正する必要があります.それは自分の属性と方法だけを拡張することができます.function Event() {}
Event.prototype = {
constructor: Event,
// key: ,listener: ( )
on: function (event, fn) {
var eventTarget = this;
// on
if(!this._events){
this._events = Object.create(null); //
}
(eventTarget._events[event] || (eventTarget._events[event] = [])).push(fn);
return eventTarget;
},
...
function Person(name){
this.name = name;
}
// mixin(Person.prototype, Event.prototype);
Object.assign(Person.prototype, Event.prototype);
mixin(Person.prototype, {
constructor: Person,
sayName: function() {
console.log(this.name);
this.emit("Person.sayName", this, name);
}
});
var p = new Person("ligang");
p.on("Person.sayName", function(args){
console.log(args.name + " emit Person.sayName event!");
});
p.sayName();
console.log(p instanceof Person); // true
console.log(p instanceof Event); // false
四、プロジェクトの中でどうやって選択しますか?上記で述べた「伝統的な方式」、「疑似的な継承」、「混入方式」の3つがプロジェクトの中で実現されていますが、どうやって選択すればいいですか?個人的には、プロジェクトでは、少なくとも2つのイベントが必要です.「バスイベント」と「支線イベント」.バスイベント:各モジュール間のリリース、購読(グローバル)を処理します.サブイベント:具体的なモジュール内部のリリース、購読(ローカル)!
// , globalEvent 、
var globalEvent = new Event();
globalEvent.on("login", ...);
globalEvent.emit("login");
// , “ ” 、
var HeaderSetting = function(){ ... }
Object.assign(HeaderSetting.prototype, Event.prototype);
var hs = new HeaderSetting();
hs.on("add", function(){
...
});
hs.addHeader = function(){
this.emit("add");
...
}