国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

jQuery 3.0 的 setter和getter 模式詳解

2019-11-20 09:30:53
字體:
供稿:網(wǎng)友

jQuery 的 setter/getter 共用一個(gè)函數(shù),通過是否傳參來表明它是何種意義。簡(jiǎn)單說傳參它是 setter,不傳它是 getter。

一個(gè)函數(shù)具有多種意義在編程語言中并不罕見,比如函數(shù)重載:一組具有相同函數(shù)名,不同參數(shù)列表的函數(shù),這組函數(shù)被稱為重載函數(shù)。重載的好處是減少了函數(shù)名的數(shù)量,避免了名字空間的污染,對(duì)于程序的可讀性也大有裨益。

函數(shù)重載主要體現(xiàn)的兩個(gè)方面,一是參數(shù)的類型、相同個(gè)數(shù)的參數(shù)類型不同可稱為函數(shù)重載;二是參數(shù)的個(gè)數(shù),個(gè)數(shù)不同也稱為函數(shù)重載。注意,重載與函數(shù)的返回值并無關(guān)系。

由于 JS 弱類型的特征,想模擬函數(shù)重載就只能通過第二種方式:參數(shù)的個(gè)數(shù)來實(shí)現(xiàn)。因此函數(shù)內(nèi)的 arguments 對(duì)象就顯得非常重要。

以下是一個(gè)示例

function doAdd() {var argsLength = arguments.lengthif (argsLength === 0) {return 0} else if (argsLength === 1) {return arguments[0] + 10} else if (argsLength === 2) {return arguments[0] + arguments[1]}}doAdd() // 0doAdd(5) // 15doAdd(5, 20) // 25

doAdd 通過判斷函數(shù)的參數(shù)個(gè)數(shù)重載實(shí)現(xiàn)了三種意義,argsLength 為 0 時(shí),直接返回 0; argsLength 為 1 時(shí),該參數(shù)與 10 相加;argsLength 為 2 時(shí)兩個(gè)參數(shù)相加。

利用函數(shù)重載特性可以實(shí)現(xiàn) setter/getter

function text() {var elem = this.elemvar argsLength = arguments.lengthif (argsLength === 0) {return elem.innerText} else if (argsLength === 1) {elem.innerText = arguments[0]}} 

以上簡(jiǎn)單的解釋了函數(shù)重載及利用它實(shí)現(xiàn) setter/getter。即"取值器"與"賦值器"合一。到底是取值還是賦值,由函數(shù)的參數(shù)決定。jQuery 的很多 API 設(shè)計(jì)大量使用了這種模式。

下圖匯總了 jQuery 中采用這種模式的所有 API,共 14 個(gè)函數(shù)

所有這些函數(shù)內(nèi)部都依賴另一個(gè)函數(shù) access, 毫不夸張的說 access 是所有這些函數(shù)的核心,是實(shí)現(xiàn) setter/getter 的核心。下面是這個(gè)函數(shù)的源碼,它是一個(gè)私有的函數(shù),外部是調(diào)用不到它的。

access 的源碼如下

// Multifunctional method to get and set values of a collection// The value/s can optionally be executed if it's a functionvar 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;};

該函數(shù)的注釋提到:這是一個(gè)多功能的函數(shù),用來獲取和設(shè)置一個(gè)集合元素的屬性和值。value 可以是一個(gè)可執(zhí)行的函數(shù)。這個(gè)函數(shù)一共不到 60 行代碼。從上往下讀,第一個(gè) if 是設(shè)置多個(gè) value 值,是一個(gè)遞歸調(diào)用。刨去這個(gè)遞歸調(diào)用,設(shè)置單個(gè)值的代碼也就不到 50 行了。寫的非常簡(jiǎn)練、耐讀。

為了理解 access 函數(shù),我畫了兩個(gè)圖

access 內(nèi)部?jī)蓚€(gè)主要分支

access 內(nèi)部的執(zhí)行流程

access 定義的形參有 7 個(gè)

1.elems 元素集合,實(shí)際調(diào)用時(shí)傳的都是 this,這里的 this 是 jQuery 對(duì)象,我們知道 jQuery 對(duì)象本身是一個(gè)集合,具有 length 屬性和索引。必傳。

2.fn 實(shí)現(xiàn) setter/getter 的函數(shù),就是說這個(gè)函數(shù)里需要有條件能判斷哪部分是 setter,哪部分是 getter。必傳。

3.key 比如 attr 和 prop 方法要傳,設(shè)置或獲取哪個(gè) key 的值。有的則不用傳,但為了占位用以 null 替代,比如 text、html 方法。可選。

4.value 僅當(dāng) setter 時(shí)要傳,即 value 為 undefined 時(shí)是 getter,否則是 setter。可選。

5.chainable 當(dāng)為 true 時(shí),進(jìn)入 setter 模式,會(huì)返回 jQuery 對(duì)象。false 則進(jìn)入 getter模式。調(diào)用時(shí)通過 arguments.length 或 arguments.length>1 傳入。

6.emptyGet 當(dāng) jQuery 對(duì)象為空時(shí),返回的結(jié)果,默認(rèn)不傳為 undefined,data 方法調(diào)用時(shí)傳的是 null。

7.raw 當(dāng) value 為函數(shù)類型時(shí) raw 為 false,否則為 true。

上面提到了 access 是 jQuery 所有 setter/getter 函數(shù)的核心,換句話說所有 14 個(gè)函數(shù) setter/getter 函數(shù)內(nèi)部都會(huì)調(diào)用 access。這也是為什么 access 有 7 個(gè)參數(shù),里面分支眾多。因?yàn)樗幚淼母鞣N條件就很多呢。但所有這些 setter/getter 有很多類同的代碼,最后還是提取一個(gè)公共函數(shù)。

為了便于理解,我把 access 的調(diào)用分類以下,便于我們理解。

1. 調(diào)用 access 時(shí),第三個(gè)參數(shù) key 傳值為 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 );},

圖示這兩個(gè)方法在 access 內(nèi)部執(zhí)行處

為什么 key 傳 null,因?yàn)?DOM API 已經(jīng)提供了。text 方法使用 el.innerText 設(shè)置或獲取;html 方法使用 innerHTML 設(shè)置或獲取(這里簡(jiǎn)單說,實(shí)際還有一些異常處理)。

2. 與第一種情況相反,調(diào)用 access 時(shí) 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 methodsjQuery.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 methodsjQuery.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 內(nèi)部執(zhí)行處

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 阜康市| 桂东县| 霸州市| 兴安县| 资源县| 伽师县| 辛集市| 蕲春县| 和林格尔县| 石棉县| 眉山市| 嘉兴市| 景泰县| 保德县| 安化县| 正宁县| 勃利县| 佛山市| 安龙县| 墨江| 阳曲县| 乌拉特后旗| 枣阳市| 潍坊市| 栖霞市| 绥芬河市| 文登市| 罗田县| 红桥区| 正阳县| 平陆县| 临澧县| 建昌县| 乳山市| 青河县| 根河市| 临城县| 天津市| 柞水县| 彭州市| 夏津县|