ホームページ > 記事 > ウェブフロントエンド > jQuery 3.0のsetterモードとgetterモードの詳細な説明
jQueryのsetter/getterは関数を共有しており、その意味はパラメータを渡すかどうかで示されます。簡単に言えば、パラメータが渡される場合はセッター、パラメータが渡されない場合はゲッターとなります。
関数のオーバーロードなど、関数が複数の意味を持つことはプログラミング言語では珍しいことではありません。同じ関数名と異なるパラメーター リストを持つ関数のグループは、オーバーロードされた関数と呼ばれます。オーバーロードの利点は、関数名の数が減り、名前空間の汚染が回避され、プログラムの可読性にも非常に有益であることです。
関数のオーバーロードは主に 2 つの側面を反映しています。1 つはパラメーターの型であり、もう 1 つはパラメーターの数が異なることです。オーバーロードは関数の戻り値とは関係がないことに注意してください。
JS の弱い型の特性により、関数のオーバーロードをシミュレートしたい場合は、2 番目の方法、つまりパラメーターの数を介してのみそれを実現できます。したがって、関数内の引数オブジェクトは非常に重要です。
以下は例です
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 は、関数のパラメータの数を決定するために argsLength が 0 の場合は 0 を直接返し、argsLength が 1 の場合は 10 にパラメータを追加します。 is 2 2 つのパラメータが追加されます。
Setter/getter は、関数のオーバーロード機能を使用して実装できます
function text() { var elem = this.elem var argsLength = arguments.length if (argsLength === 0) { return elem.innerText } else if (argsLength === 1) { elem.innerText = arguments[0] } }
上記では、関数のオーバーロードとそれを使用した Setter/getter の実装について簡単に説明しました。つまり、「値の取得者」と「割り当て者」が 1 つに結合されます。値を取得するか、値を割り当てるかは、関数のパラメーターによって決まります。多くの jQuery API 設計では、このパターンが多用されています。
次の図は、jQuery でこのモードを使用するすべての API をまとめたもので、合計 14 個の関数があります
これらの関数はすべて、内部的に別の関数へのアクセスに依存しています。アクセスがすべての核であると言っても過言ではありません。これらの関数は、setter/getter の実装の中核となります。以下はこの関数のソースコードです。これはプライベート関数であるため、外部から呼び出すことはできません。
accessのソースコードは以下の通りです
// 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; };
この関数のコメントには次のように記載されています: これは、コレクション要素の属性と値を取得および設定するために使用される多機能関数です。 value には実行可能な関数を指定できます。この関数のコードは合計 60 行未満です。上から下に読むと、最初の if は複数の値を設定するものであり、再帰呼び出しです。この再帰呼び出しを除くと、単一の値を設定するコードは 50 行未満です。文章はとても簡潔で読みやすいです。
アクセス関数を理解するために、2つの図を描きました
アクセスの2つの内部主要分岐
アクセス内部実行プロセス
アクセスによって定義される仮パラメータは7つあります
1 .elems 要素のコレクション。実際に呼び出すと、これが渡されます。ここでは、jQuery オブジェクト自体が length 属性とインデックスを持つコレクションであることがわかります。必ず合格してください。
2.fn はセッター/ゲッターを実装する関数です。つまり、この関数にはどの部分がセッターでどの部分がゲッターであるかを決定する条件が必要です。必ず合格してください。
3.key たとえば、どのキー値を attr メソッドと prop メソッドに渡して設定または取得するかです。テキスト メソッドや HTML メソッドなど、渡す必要のないものもありますが、プレースホルダーの目的で null に置き換えられます。オプション。
4.value は、セッターの場合にのみ渡される必要があります。つまり、値が未定義の場合はゲッターであり、それ以外の場合はセッターです。オプション。
5.chainable trueの場合、セッターモードに入り、jQueryオブジェクトを返します。 false を指定すると、ゲッター モードに入ります。呼び出し時にarguments.lengthまたはarguments.length>1で渡されます。
6.emptyGet jQuery オブジェクトが空の場合、返される結果はデフォルトでは未定義であり、data メソッドが呼び出されたときに null が渡されます。
7.raw value が関数型の場合、raw は false、それ以外の場合は true です。
前述したように、access は jQuery のすべての setter/getter 関数の中核です。つまり、14 個の関数 setter/getter 関数はすべて内部で access を呼び出します。これが、access に 7 つのパラメータと多くの分岐がある理由です。多くの条件に対処しなければならないからです。しかし、これらすべてのセッター/ゲッターには類似したコードが多数含まれており、最終的には共通の関数を抽出します。
理解を容易にするために、アクセス呼び出しを次のように分類しました。
1. access を呼び出すとき、3 番目のパラメーターのキーは null として渡されます。これらはそれぞれ text/html メソッドです
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 ); },
図は、これら 2 つのメソッドが access 内で実行される場所を示しています
为什么 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中文网!