引用数组的切片

时间:2012-03-01 21:25:59

标签: javascript slice

如何获取对数组切片的引用?

var A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
A.mySlice = function(l, h){return this.slice(l,h)};

var B = A.mySlice(1,5); // ["b", "c", "d", "e"]

它适用于从A派生的直接切片。但是,如何为所有切片得到它? (在这种情况下为B)

B.mySlice = function(l, h){return this.slice(l,h)};

A[3] = 33;
A.mySlice(1,5) // ["b", "c", 33, "e"] => ok
B.mySlice(0,3) // ["b", "c", "d"] => I would want ["b", "c", 33]

6 个答案:

答案 0 :(得分:3)

slice()将元素复制到新数组。

因此,在您的示例中,AB是两个完全不同的数组。因此,在一个元素中更改元素不会影响另一元素。

答案 1 :(得分:2)

我认为你不能用原生的JS数组做到这一点(好吧,不管怎么说都不是直截了当的。)

我认为最干净的方法是返回并使用自定义对象来表示切片。也许是这些方面的事情:

function ArraySlice(arr, lo, hi){
    this.arr = arr;
    this.lo = lo;
    this.hi = hi;
    this.length = hi - lo;
};
ArraySlice.prototype._contains = function(ix){
    return this.lo + ix < this.hi;
};
ArraySlice.prototype.get = function(ix){
    if (this._contains(ix)){
        return this.arr[this.lo + ix];
    }else{
        return; //undefined
    }
};
ArraySlice.prototype.set = function(ix, value){
    if (this._contains(ix)){
        return (this.arr[this.lo + ix] = value);
    }else{
        return; //undefined
    }
};

var a = [0,1,2,3,4,5];
var b = new ArraySlice(a, 1, 3);

a[2] = 17;
console.log( b.get(1) );

当然,这会丢失方便的[]语法,并且对象不再是数组,但子类化Array很烦人且容易出错,我不相信有跨浏览器方式来进行运算符重载。

答案 2 :(得分:0)

这是我看到的唯一可能的解决方案:

  • 扩展Array类并将所有数组数据存储在单例类实例中
  • 首次创建新数组时创建唯一标识符(UID)
  • 在您尝试从singleton
  • 获取数据时使用该标识符
  • 在制作切片时使用相同的标识符

然而,这可能会破坏你的许多代码。

答案 3 :(得分:0)

我很确定这在Javascript中是不可能的。此行var B = A.mySlice(1,5);将B设置为包含A个数字切片的数组对象。它没有引用A.它只是一个数组对象。

答案 4 :(得分:0)

它不是那么干净,因为你要处理函数对象,但你可以使用闭包来包装调用链回原始数组:

var A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
A.mySlice = function (l, h) { return function() { return this.slice(l, h); }; };

var B = A.mySlice(1, 5); // B() will resolve to ["b", "c", "d", "e"]
B.mySlice = function (l, h) { return function() { return this().slice(1, h) }; };

A[3] = 33;
A.mySlice(1, 5)() // ["b", "c", 33, "e"]
B.mySlice(0, 3)() // ["b", "c", 33]

答案 5 :(得分:0)

我使用 Proxy 设计了一个解决方案。这将或多或少像指向 int[]C++ 中某个位置的指针一样。它通过代理原始数组、跟踪开始和结束索引并使用引用原始数组中的相对索引的版本捕获 getset 来实现这一点。

Array.prototype.slicePtr = function(startIdx, endIdx = this.length) {
  if (startIdx < 0) {
    startIdx = this.length - startIdx - 1;
  }

  if (endIdx < 0) {
    endIdx = this.length - endIdx;
  }

  if (startIdx > endIdx) {
    throw new Error('startIdx must not be less than endIdx');
  }

  endIdx = endIdx == null ? this.length : endIdx;

  const self = this;

  function contains(idx) {
    return idx > 0 && idx < endIdx;
  }

  const proxy = new Proxy(this, {
    get(target, prop, receiver) {
      // symbols can't be converted to numbers, so handle them first
      if (typeof prop === 'symbol') {
        switch (prop) {
          case Symbol.iterator:
            return function*() {
              for (let i = startIdx; i < endIdx; i++) {
                yield self[i];
              }
            }
        }
      }

      const idx = startIdx + Number(prop);
      if (!isNaN(idx)) {
        if (!contains(idx)) {
          return undefined;
        }

        return Reflect.get(target, idx, receiver);
      }

      switch (prop) {
        case 'splice':
          return function splice(start, delCount, ...items) {
            endIdx += items.length - delCount;
            return self.splice(start + startIdx, delCount, ...items);
          }

        case 'length':
          return endIdx - startIdx;

        case 'pop':
          return function pop() {
            return proxy.splice(proxy.length - 1, 1)[0];
          }

        case 'push':
          return function push(...items) {
            proxy.splice(proxy.length, 0, ...items);
            return proxy[proxy.length - 1];
          }

        case 'shift':
          return function shift() {
            return proxy.splice(0, 1)[0];
          }

        case 'unshift':
          return function unshift(...items) {
            proxy.splice(startIdx, 0, ...items);
            return proxy[proxy.length - 1];
          }
      }

      return Reflect.get(target, prop, receiver);
    },

    set(target, prop, value, receiver) {
      if (typeof prop !== 'symbol') {
        const idx = startIdx + Number(prop);
        if (!isNaN(idx)) {
          if (Number.isFinite(idx) && Number.isInteger(idx) && Number(prop) >= proxy.length) {
            endIdx = idx + 1;
          }

          return Reflect.set(target, idx, value, receiver);
        }
      }

      switch (prop) {
        case 'length':
          endIdx = startIdx + value;
          if (endIdx > self.length) {
            self.length = endIdx;
          }

          return value;
      }

      return Reflect.set(target, prop, value, receiver);
    },
  });

  return proxy;
}

/////////////
//  TESTS  //
/////////////

let array = [];
let slice = [];

const fnRegex = /\(?\w*\)? => (?:\{\}|(.*)?);?$/;
const resultsTbl = $('#results tbody');
const makeRow = (fn) => {
  const row = $('<tr>')
    .append($('<td>').append($('<code>').text(fn.toString().replace(fnRegex, '$1'))))
    .append($('<td>').append($('<code>').text(JSON.stringify(fn(), null, 2))))
    .append($('<td>').append($('<code>').text(JSON.stringify(array, null, 2))))
    .append($('<td>').append($('<code>').text(JSON.stringify(slice, null, 2))));
  resultsTbl.append(row);
};

[
  () => array = [ 0, 1, 2, 3, 4, 5, 6, 7 ],
  () => slice = array.slicePtr(2, 5),
  () => slice[2],
  () => slice[0] = 1000,
  () => slice.length = 4,
  () => slice.push(20),
  () => slice.shift(),
  () => slice.indexOf(4),
  () => slice.filter(v => !(v % 2)),
  () => slice.map(v => v / 2),
  () => slice.splice(1, 2, 30, 40, 50),
  () => slice = slice.slicePtr(1, 4),
  () => slice[1] = 60,
  () => slice.fill(0),
  () => JSON.stringify(slice),
  () => slice.toString(),
  () => slice[7] = 'end',
].forEach(fn => makeRow(fn));
th {
  text-align: left;
  border-bottom: 1px solid darkgray;
  padding: 4px 2px;
}
td {
  padding-left: 2px;
  white-space: nowrap;
}
td:not(:last-child) {
  padding-right: 3em;
}
tr:nth-child(2n) {
  background: #eee;
}
table {
  border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="results">
  <thead><tr><th>Code</th><th>Result</th><th><code>array</code></th><th><code>[ ...slice ]</code></th></tr></thead>
  <tbody></tbody>
</table>

我唯一没有的(我认为)是console.log(slice);它总是打印 arrayAs far as I know,无法覆盖 console.log()console.dir() 将为给定对象打印的内容。

我很可能遗漏了一些我没有想到的东西,而且我从未测试过 copyWithin(),因为我仍然无法完全理解它的实际作用。不过可能有用。 ?‍♀️