jQuery의 setter/getter는 함수를 공유하며, 매개변수 전달 여부에 따라 그 의미가 표시됩니다. 간단히 말해서, 매개변수가 전달되면 setter이고, 매개변수가 전달되지 않으면 getter입니다.
함수 오버로딩과 같이 함수가 여러 의미를 갖는 것은 프로그래밍 언어에서 드문 일이 아닙니다. 함수 이름은 동일하고 매개변수 목록은 다른 함수 그룹입니다. 오버로드된 함수라고 합니다. 오버로딩의 장점은 함수 이름의 수를 줄이고 이름 공간 오염을 방지하며 프로그램 가독성에도 매우 유익하다는 것입니다.
함수 오버로딩은 주로 두 가지 측면을 반영합니다. 하나는 매개변수 유형이 다른데, 다른 하나는 매개변수 개수가 다르다는 것입니다. 이를 함수 오버로딩이라고 합니다. 오버로딩은 함수의 반환 값과 아무 관련이 없습니다.
JS의 약한 타입 특성으로 인해 함수 오버로딩을 시뮬레이션하려면 두 번째 방법인 매개변수 개수를 통해서만 수행할 수 있습니다. 따라서 함수 내의 인수 개체는 매우 중요합니다.
다음은 예시입니다
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이면 argsLength가 1이면 직접 0을 반환합니다. , 인수는 10에 추가됩니다. argsLength가 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 구현에 대한 사용법을 간략하게 설명했습니다. 즉, "가치수용자"와 "할당자"가 하나로 결합됩니다. 값을 가져올지 아니면 값을 할당할지 여부는 함수의 매개변수에 따라 결정됩니다. 많은 jQuery API 디자인에서는 이 패턴을 많이 사용합니다.
다음 그림은 jQuery에서 이 패턴을 사용하는 모든 API를 총 14개의 함수로 요약합니다.
이 모든 함수는 내부적으로 서로 의존합니다. 함수 접근, 접근은 이 모든 함수의 핵심이자 setter/getter 구현의 핵심이라고 해도 과언이 아닙니다. 다음은 이 함수의 소스코드입니다. 비공개 함수이므로 외부에서 호출할 수 없습니다.
접속 소스코드는 다음과 같습니다
// 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줄 미만입니다. 글은 매우 간결하고 읽기 쉽습니다.
액세스 기능을 이해하기 위해 두 개의 다이어그램을 그렸습니다
액세스 내의 두 가지 주요 분기
access의 내부 실행 과정
access에서 정의한 형식 매개변수는 7개의
1.elems 요소 컬렉션을 가지며, 실제로 전달되는 요소는 다음과 같습니다. 이것을 호출하면 이것이 jQuery 객체입니다. 우리는 jQuery 객체 자체가 길이 속성과 인덱스를 가진 컬렉션이라는 것을 알고 있습니다. 통과해야 합니다.
2.fn은 setter/getter 기능을 구현합니다. 즉, 이 함수에는 어느 부분이 setter이고 어느 부분이 getter인지를 결정하는 조건이 있어야 합니다. 통과해야 합니다.
3.key 예를 들어 설정하거나 가져오기 위해 attr 및 prop 메소드에 전달해야 하는 키 값은 무엇입니까? 일부는 전달할 필요가 없지만 텍스트 및 html 메소드와 같은 자리 표시자 목적으로 null로 대체됩니다. 선택 과목.
4.value는 setter인 경우에만 전달되어야 합니다. 즉, 값이 정의되지 않은 경우 getter이고, 그렇지 않으면 setter입니다. 선택 과목.
5.chainable true이면 setter 모드로 들어가 jQuery 객체를 반환합니다. getter 모드로 들어가려면 false입니다. 호출 시 인수.길이 또는 인수.길이>1로 전달됩니다.
6.emptyGet jQuery 객체가 비어 있는 경우 기본적으로 반환된 결과는 정의되지 않으며 데이터 메서드 호출 시 null이 전달됩니다.
7.raw value가 함수 유형인 경우 raw는 false이고, 그렇지 않으면 true입니다.
위에서 언급했듯이 access는 jQuery의 모든 setter/getter 함수의 핵심입니다. 즉, 14개 함수 setter/getter 함수는 모두 내부적으로 access를 호출합니다. 이것이 바로 액세스에 7개의 매개변수와 많은 분기가 있는 이유입니다. 여러 가지 조건을 다뤄야 하기 때문입니다. 그러나 이러한 모든 setter/getter는 유사한 코드를 많이 갖고 있으며 결국 공통 함수를 추출하게 됩니다.
이해를 돕기 위해 접속 호출을 다음과 같이 분류했습니다.
1. access를 호출하면 세 번째 매개변수 키가 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 ); },
이 두 메소드가 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中文网!