Сдвиг начала массива в js

В разных языках программирования это реализовано в основном через слайсы или итераторы.

golang:

a = a[1:]

C:

int* base_a = a;
a = a+1;
// ...
free(base_a);

C++:

vector<int> a = {1,2,3};
vector<int>::iterator it = a.begin();
it += 1;

C#:

// List
var a  = new List<int>(){1,2,3};
// ... or static array
// var a  = new int[]{1,2,3};

a = a.AsSpan().Slice(1);
// a = a.AsSpan()[1..];

Особый интерес в рамках этой задачи представляет js, в котором нет в общем случае единого способа получить сдвинутый на 1 подмассив, но он предоставляет такие способы в частном случае и возможности для реализации в общем.

// TypedArray
let a = new Int32Array([1, 2, 3]);
a = a.subarray(1);

// Array
const range = (arr, start, len) => {
  len = len || Math.max(0, arr.length - start);
  const end = Math.min(arr.length, start + len);
  return {
    current: start,
    length: len,
    next: function() {
      if (this.current >= end) return {done: true};
      else return {done: false, value: arr[this.current++]};
    },
    [Symbol.iterator]: function() {
      this.current = start;
      return this;
    },
    at(i) {
      return arr.at(start + i);
    },
  };
};

const range_get = (arr, start, len) => {
  len = len || Math.max(0, arr.length - start);
  return new Proxy(range(arr, start, len), {
    get(r, key) {
      if (key && key.constructor === Symbol) return r[key];
      if (key === 'length') return len;
      if (Number.isSafeInteger(+key)) return arr[+key + start];
      return r[key]; // call range methods
    },
  });
};

let a = [1, '2', {a: 3}];
let aa = range_get(a, 1);

console.log('a[0]=' + aa[0]); // '2'
console.log('a.at(0)=' + aa.at(0)); // '2'
console.log('a.len=' + aa.length); // 2

for (let ai of aa) console.log(ai); // '2', {a:3}

Как видно в представленном выше примере, при пролучении эначения элемента по индексу через скобки имеет значительный оверхеад по сравнению с получением через метод "at", поэтому предпочтителен 2-й вариант.

На практике лучше использовать функцию range и поменять в коде получение элемента через скобки на метод at или итерирование for ... of.

Неоднократно видел, как люди делают так

let arr = [1,2,3]
let r = new Proxy(arr, {/* .... */})

Такой способ предоставляет для подмассива методы оригинального массива (с индексацией оригинального массива), например, insert(index), splice(i, l, v) и так далее. Это может иметь плачевные последствия в сложных приложениях, поскольку место инициализации массива от места его использования может находиться далеко и неправильное поведение "массива" может привести в ступор любого, кто будет поддерживать такой код.

Аналогичный код на typescript (без функции range_get):

interface IArrayRange<T> extends IterableIterator<T> {
  current: number;
  readonly length: number;
  next(): IteratorResult<T>;
  at(i: number): T | undefined;
}

function range<T>(
  arr: T[],
  start: number,
  len: number = arr.length
): IArrayRange<T> {
  len = len || Math.max(0, arr.length - start);
  const end = Math.min(arr.length, start + len);
  return {
    current: start,
    length: len,
    next(): IteratorResult<T> {
      if (this.current >= end) return {value: undefined, done: true};
      else return {done: false, value: arr[this.current++]};
    },
    [Symbol.iterator](): IterableIterator<T> {
      this.current = start;
      return this;
    },
    at(i): T | undefined {
      return arr.at(start + i);
    },
  };
}

let a = [1, '2', {a: 3}];
let aa = range(a, 1);

console.log('a.at(0)=' + aa.at(0)); // '2'
console.log('a.len=' + aa.length); // 2

for (let ai of aa) console.log(ai);

Данный код можно найти в соответствующем gist.