JQuerys Setter/Getter teilen sich eine Funktion und ihre Bedeutung wird dadurch angezeigt, ob Parameter übergeben werden sollen. Einfach ausgedrückt: Wenn Parameter übergeben werden, handelt es sich um einen Setter, und wenn keine Parameter übergeben werden, handelt es sich um einen Getter.
In Programmiersprachen ist es nicht ungewöhnlich, dass eine Funktion mehrere Bedeutungen hat, wie z. B. Funktionsüberladung: Eine Gruppe von Funktionen mit demselben Funktionsnamen und unterschiedlichen Parameterlisten, diese Gruppe von Funktionen ist wird als überladene Funktion bezeichnet. Der Vorteil der Überladung besteht darin, dass sie die Anzahl der Funktionsnamen reduziert, eine Verschmutzung des Namensraums vermeidet und sich auch sehr positiv auf die Lesbarkeit des Programms auswirkt.
Funktionsüberladung spiegelt hauptsächlich zwei Aspekte wider. Die gleiche Anzahl von Parametertypen kann auch als Funktionsüberladung bezeichnet werden Man nennt es Funktionsüberladung. Beachten Sie, dass Überladung nichts mit dem Rückgabewert der Funktion zu tun hat.
Wenn Sie eine Funktionsüberladung simulieren möchten, können Sie dies aufgrund der schwachen Typeigenschaften von JS nur mit der zweiten Methode tun: der Anzahl der Parameter. Daher ist das Argumentobjekt innerhalb der Funktion sehr wichtig.
Das Folgende ist ein Beispiel
function doAdd() { var argsLength = arguments.length if (argsLength === 0) { return 0 } else if (argsLength === 1) { return arguments[0] + 10 } else if (argsLength === 2) { return arguments[0] + arguments[1] } } doAdd() // 0 doAdd(5) // 15 doAdd(5, 20) // 25
doAdd realisiert drei Bedeutungen durch Überladen, um die Anzahl der Parameter der Funktion zu bestimmen. Wenn argsLength 0 ist, gibt es 0 direkt zurück , das Argument wird zu 10 addiert; wenn argsLength 2 ist, werden die beiden Argumente addiert.
Setter/Getter kann mithilfe der Funktionsüberladungsfunktion implementiert werden
function text() { var elem = this.elem var argsLength = arguments.length if (argsLength === 0) { return elem.innerText } else if (argsLength === 1) { elem.innerText = arguments[0] } }
Oben wird kurz die Funktionsüberladung und ihre Verwendung zur Implementierung von Setter/Getter erläutert. Das heißt, der „Wertnehmer“ und der „Zedent“ werden zu einem zusammengefasst. Ob ein Wert abgerufen oder zugewiesen werden soll, wird durch die Parameter der Funktion bestimmt. Viele jQuery-API-Designs nutzen dieses Muster intensiv.
Die folgende Abbildung fasst alle APIs zusammen, die dieses Muster in jQuery verwenden, mit insgesamt 14 Funktionen
Alle diese Funktionen hängen intern voneinander ab Funktionszugriff: Man kann ohne Übertreibung sagen, dass der Zugriff der Kern all dieser Funktionen und der Kern der Setter/Getter-Implementierung ist. Das Folgende ist der Quellcode dieser Funktion. Es handelt sich um eine private Funktion, die nicht extern aufgerufen werden kann.
Der Quellcode des Zugriffs lautet wie folgt
// Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, len = elems.length, bulk = key == null; // Sets many values if ( jQuery.type( key ) === "object" ) { chainable = true; for ( i in key ) { access( elems, fn, i, key[ i ], true, emptyGet, raw ); } // Sets one value } else if ( value !== undefined ) { chainable = true; if ( !jQuery.isFunction( value ) ) { raw = true; } if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null; // ...except when executing function values } else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < len; i++ ) { fn( elems[ i ], key, raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) ); } } } return chainable ? elems : // Gets bulk ? fn.call( elems ) : len ? fn( elems[ 0 ], key ) : emptyGet; };
Der Kommentar dieser Funktion erwähnt: Dies ist eine multifunktionale Funktion, die zum Abrufen von und verwendet wird Legen Sie eine Sammlung von Elementattributen und -werten fest. value kann eine ausführbare Funktion sein. Diese Funktion umfasst insgesamt weniger als 60 Codezeilen. Von oben nach unten gelesen, besteht das erste if darin, mehrere Werte festzulegen, und ist ein rekursiver Aufruf. Ohne diesen rekursiven Aufruf umfasst der Code zum Festlegen eines einzelnen Werts weniger als 50 Zeilen. Der Schreibstil ist sehr prägnant und lesbar.
Um die Zugriffsfunktion zu verstehen, habe ich zwei Diagramme gezeichnet
Die beiden Hauptzweige innerhalb des Zugriffs
Der interne Ausführungsprozess des Zugriffs
Die durch den Zugriff definierten formalen Parameter verfügen über 7
1.elems-Elementsammlung, die tatsächlich übergeben wird Beim Aufruf ist dies das jQuery-Objekt. Wir wissen, dass das jQuery-Objekt selbst eine Sammlung mit einer Längeneigenschaft und einem Index ist. Muss passieren.
2.fn implementiert die Setter/Getter-Funktion, was bedeutet, dass diese Funktion Bedingungen haben muss, um zu bestimmen, welcher Teil ein Setter und welcher Teil ein Getter ist. Muss passieren.
3.key Beispielsweise sollten die Methoden attr und prop den Wert which key übergeben, festlegen oder abrufen. Einige müssen nicht übergeben werden, werden jedoch für Platzhalterzwecke durch null ersetzt, z. B. Text- und HTML-Methoden. Optional.
4.value sollte nur übergeben werden, wenn es sich um einen Setter handelt, d. h. wenn der Wert undefiniert ist, handelt es sich um einen Getter, andernfalls handelt es sich um einen Setter. Optional.
5.chainable Wenn true, wechselt es in den Setter-Modus und gibt ein jQuery-Objekt zurück. false, um in den Getter-Modus zu wechseln. Wird beim Aufruf in arguments.length oder arguments.length>1 übergeben.
6.emptyGet Wenn das jQuery-Objekt leer ist, ist das zurückgegebene Ergebnis standardmäßig undefiniert und beim Aufruf der Datenmethode wird null übergeben.
7.raw Wenn value ein Funktionstyp ist, ist raw falsch, andernfalls ist es wahr.
Wie oben erwähnt, ist der Zugriff der Kern aller Setter/Getter-Funktionen in jQuery. Mit anderen Worten: Alle 14 Setter/Getter-Funktionen rufen den Zugriff intern auf. Aus diesem Grund hat der Zugriff 7 Parameter und viele Zweige. Weil es mit vielen Bedingungen klarkommen muss. Aber alle diese Setter/Getter haben viel ähnlichen Code und extrahieren am Ende eine gemeinsame Funktion.
Um das Verständnis zu erleichtern, habe ich die Zugriffsaufrufe wie folgt klassifiziert, um unser Verständnis zu erleichtern.
1. Beim Aufruf von Access wird der dritte Parameterschlüssel als Null übergeben, nämlich die Text-/HTML-Methode
text: function( value ) { return access( this, function( value ) { return value === undefined ? jQuery.text( this ) : this.empty().each( function() { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { this.textContent = value; } } ); }, null, value, arguments.length ); }, html: function( value ) { return access( this, function( value ) { var elem = this[ 0 ] || {}, i = 0, l = this.length; if ( value === undefined && elem.nodeType === 1 ) { return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { value = jQuery.htmlPrefilter( value ); try { for ( ; i < l; i++ ) { elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } elem = 0; // If using innerHTML throws an exception, use the fallback method } catch ( e ) {} } if ( elem ) { this.empty().append( value ); } }, null, value, arguments.length ); },
Das Diagramm zeigt, wo diese beiden Methoden innerhalb von Access ausgeführt werden
为什么 key 传 null,因为 DOM API 已经提供了。text 方法使用 el.innerText 设置或获取;html 方法使用 innerHTML 设置或获取(这里简单说,实际还有一些异常处理)。
2. 与第一种情况相反,调用 access 时 key 值传了且不为 null。除了 text/html 外的其它 setter 都是如此
attr: function( name, value ) { return access( this, jQuery.attr, name, value, arguments.length > 1 ); }, prop: function( name, value ) { return access( this, jQuery.prop, name, value, arguments.length > 1 ); }, // Create scrollLeft and scrollTop methods jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) { var top = "pageYOffset" === prop; jQuery.fn[ method ] = function( val ) { return access( this, function( elem, method, val ) { var win = getWindow( elem ); if ( val === undefined ) { return win ? win[ prop ] : elem[ method ]; } if ( win ) { win.scrollTo( !top ? val : win.pageXOffset, top ? val : win.pageYOffset ); } else { elem[ method ] = val; } }, method, val, arguments.length ); }; } ); css: function( name, value ) { return access( this, function( elem, name, value ) { var styles, len, map = {}, i = 0; if ( jQuery.isArray( name ) ) { styles = getStyles( elem ); len = name.length; for ( ; i < len; i++ ) { map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); } return map; } return value !== undefined ? jQuery.style( elem, name, value ) : jQuery.css( elem, name ); }, name, value, arguments.length > 1 ); } // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { // Margin is only for outerHeight, outerWidth jQuery.fn[ funcName ] = function( margin, value ) { var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); return access( this, function( elem, type, value ) { var doc; if ( jQuery.isWindow( elem ) ) { // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729) return funcName.indexOf( "outer" ) === 0 ? elem[ "inner" + name ] : elem.document.documentElement[ "client" + name ]; } // Get document width or height if ( elem.nodeType === 9 ) { doc = elem.documentElement; // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], // whichever is greatest return Math.max( elem.body[ "scroll" + name ], doc[ "scroll" + name ], elem.body[ "offset" + name ], doc[ "offset" + name ], doc[ "client" + name ] ); } return value === undefined ? // Get width or height on the element, requesting but not forcing parseFloat jQuery.css( elem, type, extra ) : // Set width or height on the element jQuery.style( elem, type, value, extra ); }, type, chainable ? margin : undefined, chainable ); }; } ); } ); data: function( key, value ) { var i, name, data, elem = this[ 0 ], attrs = elem && elem.attributes; // Gets all values if ( key === undefined ) { if ( this.length ) { data = dataUser.get( elem ); if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { // Support: IE 11 only // The attrs elements can be null (#14894) if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } } dataPriv.set( elem, "hasDataAttrs", true ); } } return data; } // Sets multiple values if ( typeof key === "object" ) { return this.each( function() { dataUser.set( this, key ); } ); } return access( this, function( value ) { var data; // The calling jQuery object (element matches) is not empty // (and therefore has an element appears at this[ 0 ]) and the // `value` parameter was not undefined. An empty jQuery object // will result in `undefined` for elem = this[ 0 ] which will // throw an exception if an attempt to read a data cache is made. if ( elem && value === undefined ) { // Attempt to get data from the cache // The key will always be camelCased in Data data = dataUser.get( elem, key ); if ( data !== undefined ) { return data; } // Attempt to "discover" the data in // HTML5 custom data-* attrs data = dataAttr( elem, key ); if ( data !== undefined ) { return data; } // We tried really hard, but the data doesn't exist. return; } // Set the data... this.each( function() { // We always store the camelCased key dataUser.set( this, key, value ); } ); }, null, value, arguments.length > 1, null, true ); },
图示这些方法在 access 内部执行处
更多jQuery 3.0 的 setter和getter 模式详解相关文章请关注PHP中文网!