常识 共享 合作 bodog官网

    bodog官网专心于网页材料下载,供给博狗博彩、网页规划、ps材料、图片材料等,服务于【个人站长】【网页规划师】和【web开发从业者】的代码材料与规划材料网站。

    bodog官网供给网页材料下载、博狗博彩
    常识 共享 合作!

    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  bodog官网 http://www.3mbodoglv.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监听数组改动由bodog官网收集整理,您能够自在传达,请自动带上本文链接

    bodog官网便是免费共享,觉得有用就多来支撑一下,没有能帮到您,懒人也只能表示遗憾,期望有一天能帮到您。

    javascript监听数组改动-最新谈论