Heim >Web-Frontend >js-Tutorial >Eine kurze Diskussion über den Nodejs-Beobachter pattern_node.js
1. Vorwort
Ich verwende Nodejs seit einiger Zeit. Ich werde die API überprüfen und weitere neue Funktionen verwenden, um ein höheres Maß an Beherrschung zu erreichen. Diese Zusammenfassung der API unterscheidet sich von der einfachen chinesischen Version der englischen Version . Ich werde weitere Erweiterungen vornehmen und hoffe, dass sie für alle hilfreich sind. Beginnen wir mit den Kernereignissen
Nodejs‘ Events implementiert einen Beobachtermodus, der den Kernmechanismus von Nodejs unterstützt, und http/fs/mongoose usw. erben alle Events und können Abhörereignisse hinzufügen. Dieses Entwurfsmuster wird häufig in Ideen zur clientseitigen Komponentenprogrammierung verwendet. Lassen Sie uns dieses Muster zunächst kurz verstehen.
Das erste Mal, dass ich mit dem Beobachtermuster in Kontakt kam, war im Ext.util.observable-Quellcode des Extjs-Frameworks. Ich war damals neu bei js und hatte das Gefühl, dass dieses Muster sehr leistungsfähig war Das erste Entwurfsmuster, mit dem ich in Kontakt kam, wurde später auch in den Quellcode von underscore.js aufgenommen. Da Letzteres einfacher und eleganter ist, folge ich beim Schreiben von Komponenten grundsätzlich dieser Idee.
Der Beobachtermodus besteht darin, einem Objekt ein Überwachungsereignis hinzuzufügen, z. B. on('show', callback), das vom Objekt ausgelöst wird, wenn es die Bedingungen wie show erfüllt. Der Browser selbst hat eine Überwachung implementiert Mechanismus für den Dom.
Wenn wir eine Keyup-Überwachung für die Eingabe hinzufügen, besteht der Zweck darin, den Wert auszugeben
$( 'input' ).on( 'keyup', function(){ console.log( this.value ); } );
Wenn Sie Inhalte auf diese Weise eingeben, wird ihr Wert im Protokoll ausgegeben.
Aber wenn wir eine Komponente wie Dialog erstellen, wie können wir dann die am häufigsten verwendeten Ein-/Ausblenden-Ereignisse überwachen?
Der grundlegende Ansatz besteht darin, den Rückruf direkt während der Instanziierung zu konfigurieren, z. B.
var dialog = new Dialog({ content: '这里是弹出框的内容', show: function(){ console.log( '当弹框时输出此段内容' ); } });
Dies kann auch verwendet werden, ist aber offensichtlich nicht flexibel genug. Wie kann ich die dialogähnliche Eingabe gestalten und jederzeit Ereignisse hinzufügen?
2. Implementierung des Beobachtermodus
Implementieren Sie zunächst das Events-Objekt, das eine grundlegende Überwachung und Auslösung von Emitting-Ereignissen ermöglicht. Ereignisse werden in den _events des Objekts in Form von JSON gestapelt
var Events = { on: function( name, callback){ this._events = this._events || {}; this._events[ name ] = this._events[ name ] || []; this._events[ name ].push( callback ); }, emit: function( name ){ this._events = this._events || {}; var args = Array.prototype.slice.call( arguments, 1 ), me = this; if( this._events[ name ] ){ $.each( this._events[ name ], function( k, v ){ v.call( me, args ); } ) } } }
Eine weitere abstrakte Funktion wird zum Kopieren von Attributen für Objekte verwendet
function extend( source ){ var args = Array.prototype.slice.call( arguments, 1 ); for( var i = 0, parent; parent = args[i]; i++ ){ for( var prop in parent ){ source[ prop ] = parent[ prop ]; } } }
Implementieren Sie einen Dialog,
Implementiert nur die Erstellung; Methode: anzeigen / ausblenden; Ereignis: anzeigen / ausblenden;
.dialog{ position: fixed; top: 50%; left: 50%; margin: -50px 0 0 -100px; width: 200px; height: 120px; background: #fff; border: 5px solid #afafaf; }
var Dialog = function( config ){ this.config = config; this.init( this.config ); };
extend( Dialog.prototype, { init: function( config ){ this.render( config ) }, render: function( config ){ this.el = $( '<div>' ).addClass( 'dialog' ); this.el.html( config.content ); $( 'body' ).append( this.el ); }, show: function( param ){ this.el.fadeIn(); this.emit( 'show', param ); }, hide: function( param ){ this.el.fadeOut(); this.emit( 'hide', param ); } }, Events );
var dialog = window.dialog = new Dialog({ content: 'dialog one' }); dialog.on( 'show', function( txt ){ console.log( 'dialog show one ' + txt ); } ); //do something dialog.on( 'show', function( txt ){ console.log( 'dialog show two ' + txt ); } ); //do something dialog.on( 'show', function( txt ){ console.log( 'dialog show three ' + txt ); } ); //do something dialog.on( 'hide', function( txt ){ console.log( 'dialog hide one ' + txt ); } ); //do something dialog.on( 'hide', function( txt ){ console.log( 'dialog hide two ' + txt ); } ); //do something dialog.on( 'hide', function( txt ){ console.log( 'dialog hide three ' + txt ); } );
Wenn dialog.show() ausgeführt wird, werden drei entsprechende Protokolle ausgegeben. Die hinzugefügten Ereignisse werden in dialog._events gespeichert, wie in der Abbildung
dargestellt
Die drei hinzugefügten Shows werden alle erfolgreich ausgegeben und die Ereignisse werden im _events-Attribut gespeichert
nodejs Events implementiert diesen Prozess ebenfalls.
3. Struktur
var Events = require( 'events' ); console.log( Events ); /* 输出如下数据,可以看出 Events指向其EventEmiter { [Function: EventEmitter] EventEmitter: [Circular], usingDomains: [Getter/Setter], defaultMaxListeners: 10, init: [Function], listenerCount: [Function] } */ var myEmitter = new Events(); console.log( myEmitter ); /* { domain: null, _events: {}, //可以看到实例本身也有_events属性,添加的监听的事件就保存在这里 _maxListeners: undefined} */ console.log( myEmitter.__proto__ ); /* { domain: undefined, _events: undefined, _maxListeners: undefined, setMaxListeners: [Function: setMaxListeners], emit: [Function: emit], addListener: [Function: addListener], on: [Function: addListener], once: [Function: once], removeListener: [Function: removeListener], removeAllListeners: [Function: removeAllListeners], listeners: [Function: listeners] } */ myEmitter.on( 'show', function( txt ){ console.log( 'one ' + txt )}) myEmitter.on( 'show', function( txt ){ console.log( 'tow ' + txt )}) myEmitter.on( 'hide', function( txt ){ console.log( 'one ' + txt )}) myEmitter.emit( 'show', 'show' ); myEmitter.setMaxListeners( 10 ); console.log( myEmitter ); /* { domain: null, _events: { show: [ [Function], [Function] ], hide: [Function] }, //添加后的事情,以json形式存放 _maxListeners: 10 } */
4. API
Die bereitgestellten Methoden umfassen „on“, die Abkürzung für „addListener“. Sie fügen der Instanz alle Listening-Ereignisse hinzu
property _events: undefined, //以压栈形式存放on进来的事件 _maxListeners: undefined //设置最大监听数,超出提warn ---------------------------------------------------------------------------------------------------------------- method setMaxListeners: [Function: setMaxListeners], /*设置私有属性_maxListeners的值,默认Events会在当某监听事件多于10个时发现警告(见上面Events.defaultMaxListeners),以防止内存泄露,如 (node) warning: possible EventEmitter memory leak detected. 11 show listeners added. Use emitter.setMaxListeners() to increase limit. 但这只是个友好的提醒,可以通过设置最大监听数来规避这个问题 myEmitter.setMaxListeners( 20 ); */ emit: [Function: emit], /*触发监听事件 emitter.emit( event, [arg1], [arg2], ... ) 如myEmitter.on( 'show', 'prompt content' ); 参数1为事件名,参数二供on回调里的参数 */ addListener: [Function: addListener], /* 添加监听事件 emitter.addListener( event, listener ); 如 myEmitter.addListener( 'show', function( txt ){ console.log( txt ) } ); 参数一是事件名,参数二是对应的回调,回调里的参数就是 emit里的arguments.prototype.slice.call(1); */ on: [Function: addListener], /* 是addListener简写 */ once: [Function: once], /* 作用同 on,不过emit一次后就失效了 emitter.once( event, listener ); 如 myEmitter.once( 'show', function( txt ){ console.log( txt ) } ); 当myEmitter.emit执行第二次时没有输出 */ removeListener: [Function: removeListener], /* 移除指定事件的指定回调,此时回调不能再用匿名函数。 emitter.removeListener( event, listener ); 如 function show( txt ){ console.log( txt ) }; myEmitter.on( 'show', show ); console.log( myEmitter._events ); // { show: [ Function: show ] } myEmitter.removeListener( 'show', show ); console.log( myEmitter._events ); // {} */ removeAllListeners: [Function: removeAllListeners], /* 删除指定事件的所有回调 emitter.removeAllListeners( [ event ] ); 如 myEmitter.removeAllListeners( 'show' ); //删除所有show监听 myEmitter.removeAllListeners(); //删除所有监听 */ listeners: [Function: listeners] /* 查看指定监听 emitter.listeners( event ); 如 myEmitter.listeners( 'show' ); //返回一个数组 同我们前面使用的 myEmitter._events[ 'show' ] */ 另外Events类本身提供了一个方法 Events.listenerCount( emitter, event ); 获取指定实例下指定监听数 如 Event.listenerCount( myEmitter, 'show' ) ----------------------------------------------------------------------------------------------- 还有两个event newListener / remoteListener,分别应用于为实例添加( on / once )和删除( removeListener ) 操作。 emitter.on( event, listener ); emitter.on( 'newListener', function( event, listener ){ console.log( emitter.listeners( 'show' ) ); //注意,此时监听还并没有添加到 emitter.listeners console.log( arguments ); }); emitter.on( 'removeListener', function(){ console.log( emitter.listeners( 'show' ) ); console.log( arguments ); })5. Bewerbung
Um Ereignisse zu verwenden, instanziieren Sie sie normalerweise einfach direkt, wie im API-Abschnitt oben gezeigt
Wenn wir jedoch auch eine Komponente auf der NodeJS-Seite implementieren, wie zum Beispiel den vorherigen Dialog, wie können wir dann dafür sorgen, dass Dialog auch die Funktion von Ereignissen hat? Erweiterungslösung, die mit Extjs implementiert werden kann
Dialog-Builder erstellen
var Dialog = function(){ //do something } //抽象apply函数,提供属性的深度复制,同上面的extend function apply( source ){ var args = Array.prototype.slice.call( arguments, 1 ); for( var i = 0, parent; parent = args[i]; i++ ){ for( var prop in parent ){ source[ prop ] = parent[ prop ]; } } } //抽象extend函数,用于实现继承 var extend = function(){ // inline overrides var io = function(o){ for(var m in o){ this[m] = o[m]; } }; var oc = Object.prototype.constructor; return function(sb, sp, overrides){ if(typeof sp == 'object'){ overrides = sp; sp = sb; sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);}; } var F = function(){}, sbp, spp = sp.prototype; F.prototype = spp; sbp = sb.prototype = new F(); sbp.constructor=sb; sb.superclass=spp; if(spp.constructor == oc){ spp.constructor=sp; } sb.override = function(o){ apply(sb, o); }; sbp.superclass = sbp.supr = (function(){ return spp; }); sbp.override = io; apply(sb, overrides); sb.extend = function(o){return extend(sb, o);}; return sb; }; }(); //将Events属性继承给Dialog Dialog = extend( Dialog, Events ); //为Dialog新增 method show,其内触发 event show Dialog.prototype.show = function( txt ){ this.emit( 'show', txt ); } var dialog = new Dialog(); //添加监听事件show dialog.on( 'show', function(txt){ console.log( txt )}); //执行method show时,就会触发其内定义的show events,输出 this is show dialog.show( 'this is show' );Auf diese Weise wird der Ereignismechanismus für eine Komponente implementiert. Wenn die Methode aufgerufen wird, wird das Ereignis ausgelöst
Nodejs bietet einen guten Überwachungsmechanismus und wird auch in allen seinen Modulen verwendet. Es unterstützt beispielsweise den charakteristischsten E/A-Modus von nodejs. Wenn wir beispielsweise den HTTP-Dienst starten, überwacht er dessen Verbindung/Schließung http.request Überwachen Sie Daten/Ende usw. Das Verständnis des Überwachungsmechanismus ist die Grundlage für das Lernen und Verstehen von NodeJS und trägt auch zur Verbesserung von Programmierideen bei.