如何获取对数组切片的引用?
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]
答案 0 :(得分:3)
slice()
将元素复制到新数组。
因此,在您的示例中,A
和B
是两个完全不同的数组。因此,在一个元素中更改元素不会影响另一元素。
答案 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)
这是我看到的唯一可能的解决方案:
然而,这可能会破坏你的许多代码。
答案 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++
中某个位置的指针一样。它通过代理原始数组、跟踪开始和结束索引并使用引用原始数组中的相对索引的版本捕获 get
和 set
来实现这一点。
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)
;它总是打印 array
。 As far as I know,无法覆盖 console.log()
和 console.dir()
将为给定对象打印的内容。
我很可能遗漏了一些我没有想到的东西,而且我从未测试过 copyWithin()
,因为我仍然无法完全理解它的实际作用。不过可能有用。 ?♀️