<meter id="7ntxv"></meter>

<b id="7ntxv"><listing id="7ntxv"><ol id="7ntxv"></ol></listing></b>
<nobr id="7ntxv"></nobr><mark id="7ntxv"></mark>

    <p id="7ntxv"><menuitem id="7ntxv"><font id="7ntxv"></font></menuitem></p>
    <sub id="7ntxv"><menuitem id="7ntxv"><meter id="7ntxv"></meter></menuitem></sub>

    <mark id="7ntxv"></mark>
    <meter id="7ntxv"><var id="7ntxv"></var></meter>

    知識 分享 互助 懶人建站

      懶人建站專注于網頁素材下載,提供網站模板、網頁設計、ps素材、圖片素材等,服務于【個人站長】【網頁設計師】和【web開發從業者】的代碼素材與設計素材網站。

      懶人建站提供網頁素材下載、網站模板
      知識 分享 互助!

      javascript監聽數組變化

      作者:佳明媽 來源:oschina 2017-05-31 人氣:
      監聽javascript數組變化,從而改變數據是mvvm框架必備功能,下面嘗試使用不同的方法來實現js數組變化的監聽

      監聽javascript數組變化,從而改變數據是mvvm框架必備功能,下面嘗試使用不同的方法來實現js數組變化的監聽。

      javascript監聽數組變化思路

      1、定義變量arrayProto接收Array的prototype
      2、定義變量arrayMethods,通過Object.create()方法繼承arrayProto
      3、重新封裝數組中push,pop等常用方法。(這里我們只封裝我們需要監聽的數組的方法,并不做JavaScript原生Array中原型方法的重寫的這么一件暴力的事情)
      4、其他js數組變化監聽方法?

      js監聽數組變化實現方法

      這里我們首先需要確定的一件事情就是,我們只需要監聽我們需要監聽的數據數組的一個變更,而不是針對原生Array的一個重新封裝。

      其實代碼實現起來會比較簡短,這一部分代碼我會直接帶著注釋貼出來

      // 獲取Array原型
      const arrayProto = Array.prototype;
      const arrayMethods = Object.create(arrayProto);
      const newArrProto = [];
      [
        'push',
        'pop',
        'shift',
        'unshift',
        'splice',
        'sort',
        'reverse'
      ].forEach(method => {
        // 原生Array的原型方法
        let original = arrayMethods[method];
      
        // 將push,pop等方法重新封裝并定義在對象newArrProto的屬性上
        // 這里需要注意的是封裝好的方法是定義在newArrProto的屬性上而不是其原型屬性
        // newArrProto.__proto__ 沒有改變
        newArrProto[method] = function mutator() {
          console.log('監聽到數組的變化啦!');
      
          // 調用對應的原生方法并返回結果(新數組長度)
          return original.apply(this, arguments);
        }
      })
      
      let list = [1, 2];
      // 將我們要監聽的數組的原型指針指向上面定義的空數組對象
      // newArrProto的屬性上定義了我們封裝好的push,pop等方法
      list.__proto__ = newArrProto;
      list.push(3);  // 監聽到數組的變化啦! 3
      
      // 這里的list2沒有被重新定義原型指針,所以這里會正常執行原生Array上的原型方法
      let list2 = [1, 2];
      list2.push(3);  // 3

      目前為止我們已經實現了數組的監聽。從上面我們看出,當我們將需要監聽的數組的原型指針指向newArrProto對象上的時候(newArrProto的屬性上定義了我們封裝好的push,pop等方法)。這樣做的好處很明顯,不會污染到原生Array上的原型方法。

      其他js數組變化監聽方法

      1、分析實現的機制

      從上面我們看出,其實我們做了一件非常簡單的事情,首先我們將需要監聽的數組的原型指針指向newArrProto,然后它會執行原生Array中對應的原型方法,與此同時執行我們自己重新封裝的方法。

      那么問題來了,這種形式咋這么眼熟呢?這不就是我們見到的最多的繼承問題么?子類(newArrProto)和父類(Array)做的事情相似,卻又和父類做的事情不同。但是直接修改__proto__隱式原型指向總感覺心里怪怪的(因為我們可能看到的多的還是prototype),心里不(W)舒(T)服(F)。

      那么接下來的事情就是嘗試用繼承(常見的prototype)來實現數組的變更監聽

      2、利用ES6的extends實現

      首先這里我們會通過ES6的關鍵字extends實現繼承完成Array原型方法的重寫,咱總得先用另外一種方式來實現一下我們上面實現的功能,證明的確還有其他方法可以做到這件事。OK,廢話不多說,直接看代碼

      class NewArray extends Array {
        constructor(...args) {
          // 調用父類Array的constructor()
          super(...args)
        }
        push (...args) {
          console.log('監聽到數組的變化啦!');
      
          // 調用父類原型push方法
          return super.push(...args)
        }
        // ...
      }
      
      let list3 = [1, 2];
      
      let arr = new NewArray(...list3);
      console.log(arr)
      // (2) [1, 2]
      
      arr.push(3);
      // 監聽到數組的變化啦!
      console.log(arr)
      // (3) [1, 2, 3]

      3、ES5及以下的方法能實現js監聽數組變化嗎?

      OK,終于要回到我們常見的帶有prototype的繼承了,看看它究竟能不能也實現這件事情呢。這里我們直接上最優雅的繼承方式-寄生式組合繼承,看看能不能搞定這件事情。代碼如下

      /**
       * 寄生式繼承 繼承原型
       * 傳遞參數 subClass 子類
       * 傳遞參數 superClass 父類
       */
      function inheritObject(o){
        //聲明一個過渡函數
        function F(){}
        //過渡對象的原型繼承父對象
        F.prototype = o;
        return new F();
      }
      function inheritPrototype(subClass,superClass){
        //復制一份父類的原型副本保存在變量
        var p = inheritObject(superClass.prototype);
        //修正因為重寫子類原型導致子類的constructor指向父類
        p.constructor = subClass;
        //設置子類的原型
        subClass.prototype = p;
      }
      
      function ArrayOfMine (args) {
        Array.apply(this, args);
      }
      inheritPrototype(ArrayOfMine, Array);
      // 重寫父類Array的push,pop等方法
      ArrayOfMine.prototype.push = function () {
        console.log('監聽到數組的變化啦!');
        return Array.prototype.push.apply(this, arguments);
      }
      var list4 = [1, 2];
      var newList = new ArrayOfMine(list4);
      console.log(newList, newList.length, newList instanceof Array, Array.isArray(newList));
      newList.push(3);
      console.log(newList, newList.length, newList instanceof Array, Array.isArray(newList));

      目前我們這么看來,的的確確是利用寄生式組合繼承完成了一個類的繼承,那么console.log的結果又是如何的呢?是不是和我們預想的一樣呢,直接看圖說話吧
      ES5及以下的方法能實現js監聽數組變化嗎?

      我擦嘞,這特么什么鬼,教練,我們說好的,不是這個結果。這是典型的買家秀和賣家秀嗎?

      那么我們來追溯一下為什么會是這種情況,我們預想中的情況應該是這樣的

      newList => [1, 2]  newList.length => 2  Array.isArray(newList) => true

      push執行之后的理想結果

      newList => [1, 2, 3]  newList.length => 3  Array.isArray(newList) => true
      

      我們先拋棄Array的apply之后的結果,我們先用同樣的方式繼承我們自定義的父類Father,代碼如下

      function inheritObject(o){
        function F(){};
        F.prototype = o;
        return new F();
      }
      function inheritPrototype(subClass,superClass){
        var p = inheritObject(superClass.prototype);
        p.constructor = subClass;
        subClass.prototype = p;
      }
      
      function Father() {
        // 這里我們暫且就先假定參數只有一個
        this.args = arguments[0];
        return this.args;
      }
      Father.prototype.push = function () {
        this.args.push(arguments);
        console.log('我是父類方法');
      }
      function ArrayOfMine () {
        Father.apply(this, arguments);
      }
      inheritPrototype(ArrayOfMine, Father);
      // 重寫父類Array的push,pop等方法
      ArrayOfMine.prototype.push = function () {
        console.log('監聽到數組的變化啦!');
        return Father.prototype.push.apply(this, arguments);
      }
      var list4 = [1, 2];
      var newList = new ArrayOfMine(list4, 3);
      console.log(newList, newList instanceof Father);
      newList.push(3);
      console.log(newList, newList instanceof Father);

      結果如圖

      結果和我們之前預想的是一樣的,我們自己定義的類的話,這種做法是可以行的通的,那么問題就來了,為什么將父類改成Array就行不通了呢?

      為了搞清問題,查閱各種資料后。得出以下結論:
      因為Array構造函數執行時不會對傳進去的this做任何處理。不止Array,String,Number,Regexp,Object等等JS的內置類都不行。。這也是著名問題 ES5及以下的JS無法完美繼承數組 的來源,不清楚的小伙伴可以Google查查這個問題。那么,為什么不能完美繼承呢?

      1、數組有個響應式的length,一方面它會跟進你填入的元素的下表進行一個增長,另一方面如果你將它改小的話,它會直接將中間的元素也刪除掉

      var arr1 = [1];
      arr1[5] = 1;
      console.log(arr1.length === 6);  // true
      // 以及
      var arr2 = [1,2,3];
      arr2.length = 1
      console.log(arr2);
      // [1] 此時元素2,3被刪除了

      2、數組內部的[[class]] 屬性,這個屬性是我們用Array.isArray(someArray)和Object.prototype.String.call(someArray) 來判定someArray是否是數組的根源,而這又是內部引擎的實現,用任何JS方法都是無法改變的。而為啥要用這兩種方法進行數組的判定,相信大家從前面的代碼結果可以看出來,利用instanceof去判定是否為數組,結果是有問題的。

      因為數組其響應式的length屬性以及內部的[[class]]屬性我們無法再JS層面實現,這就導致我們無法去用任何一個對象來“模仿”一個數組,而我們想要創建一個ArrayOfMine繼承Array的話又必須直接用Array的構造函數,而上面我提到了Array構造函數執行時是不會對傳進去的this做任何處理,也就是說這樣你根本就不能繼承他。而利用__proto__隱式原型的指針變更卻能實現,因為他是一個非標準的屬性(已在ES6語言規范中標準化),詳請請點擊鏈接__proto__。

      所以要實現最上面我們實現的功能,我們還是需要用到__proto__屬性。變更后代碼如下

      function inheritObject(o){
        function F(){}
        F.prototype = o;
        return new F();
      }
      function inheritPrototype(subClass,superClass){
        var p = inheritObject(superClass.prototype);
        p.constructor = subClass;
        subClass.prototype = p;
      }
      
      function ArrayOfMine () {
        var args = arguments
          , len = args.length
          , i = 0
          , args$1 = [];   // 保存所有arguments
        for (; i < len; i++) {
          // 判斷參數是否為數組,如果是則直接concat
          if (Array.isArray(args[i])) {
            args$1 = args$1.concat(args[i]);
          }
          // 如果不是數組,則直接push到
          else {
            args$1.push(args[i])
          }
        }
        // 接收Array.apply的返回值,剛接收的時候arr是一個Array
        var arr = Array.apply(null, args$1);
        // 將arr的__proto__屬性指向 ArrayOfMine的 prototype
        arr.__proto__ = ArrayOfMine.prototype;
        return arr;
      }
      inheritPrototype(ArrayOfMine, Array);
      // 重寫父類Array的push,pop等方法
      ArrayOfMine.prototype.push = function () {
        console.log('監聽到數組的變化啦!');
        return Array.prototype.push.apply(this, arguments);
      }
      var list4 = [1, 2];
      var newList = new ArrayOfMine(list4, 3);
      console.log(newList, newList.length, newList instanceof Array, Array.isArray(newList));
      newList.push(4);
      console.log(newList, newList.length, newList instanceof Array, Array.isArray(newList));

      結果如圖

      自此,我所知道幾種實現數組監聽的方法便得于實現了。

      js數組變化監聽總結

      總結以上幾點方案,完整的數組監聽代碼如下

      // Define Property  懶人建站 http://www.waidid2022.com/
      function def (obj, key, val, enumerable) {
        Object.defineProperty(obj, key, {
          value: val,
          enumerable: !!enumerable,
          configurable: true,
          writable: true
        })
      }
      // observe array
      let arrayProto = Array.prototype;
      let arrayMethods = Object.create(arrayProto);
      [
        'push',
        'pop',
        'shift',
        'unshift',
        'splice',
        'sort',
        'reverse'
      ].forEach(method => {
        // 原始數組操作方法
        let original = arrayMethods[method];
        def(arrayMethods, method, function () {
          let arguments$1 = arguments;
          let i = arguments.length;
          let args = new Array(i);
      
          while (i--) {
            args[i] = arguments$1[i]
          }
          // 執行數組方法
          let result = original.apply(this, args);
          // 因 arrayMethods 是為了作為 Observer 中的 value 的原型或者直接作為屬性,所以此處的 this 一般就是指向 Observer 中的 value
          // 當然,還需要修改 Observer,使得其中的 value 有一個指向 Observer 自身的屬性,__ob__,以此將兩者關聯起來
          let ob = this.__ob__;
          // 存放新增數組元素
          let inserted;
          // 為add 進arry中的元素進行observe
          switch (method) {
            case 'push':
              inserted = args;
              break;
            case 'unshift':
              inserted = args;
              break;
            case 'splice':
              // 第三個參數開始才是新增元素
              inserted = args.slice(2);
              break;
          }
          if (inserted) {
            ob.observeArray(inserted);
          }
          // 通知數組變化
          ob.dep.notify();
          // 返回新數組長度
          return result;
        })
      
      })

      原文:https://my.oschina.net/qiangdada/blog/911252#comment-list
      github:https://github.com/xuqiang521/overwrite/tree/master/my-mvvm

      ↓ 查看全文

      javascript監聽數組變化由懶人建站收集整理,您可以自由傳播,請主動帶上本文鏈接

      懶人建站就是免費分享,覺得有用就多來支持一下,沒有能幫到您,懶人也只能表示遺憾,希望有一天能幫到您。

      javascript監聽數組變化-最新評論

      亚洲免费的黄色网站_黄色网站在线放久操射视频_A片www.黄色网站成年人_天天干 天天操天天干

      <meter id="7ntxv"></meter>

      <b id="7ntxv"><listing id="7ntxv"><ol id="7ntxv"></ol></listing></b>
      <nobr id="7ntxv"></nobr><mark id="7ntxv"></mark>

        <p id="7ntxv"><menuitem id="7ntxv"><font id="7ntxv"></font></menuitem></p>
        <sub id="7ntxv"><menuitem id="7ntxv"><meter id="7ntxv"></meter></menuitem></sub>

        <mark id="7ntxv"></mark>
        <meter id="7ntxv"><var id="7ntxv"></var></meter>