무음판에서 데이터를 처리하는 라이브러리 및 Stage 2 Record &Tuple

JavaScript의 무음 슬라이스, 무음 슬라이스


JavaScript에서 원재료는 이세계적이다. 즉, 변경할 수 없다는 것이다.
https://developer.mozilla.org/ja/docs/Glossary/Primitive
다른 한편, 대상은 기본적으로 정음판, 즉 수정할 수 있다는 것이다.Object.freeze는 동결할 수 있지만 액세서리 속성이 문제없이 작동하면 [[Prototype]]의 변경에 영향을 줄 수 있다.동결됐다고 해서 완전히 이구체라고 할 수는 없겠죠.
let a = 1;
const obj = Object.freeze({
  get a() { return a; },
  set a(val) { a = val; },
});

console.log(obj.a); // => 1
++obj.a;
console.log(obj.a); // => 2
const prototype = { a: 1 };
const obj = Object.freeze(Object.create(prototype));

console.log(obj.a); // => 1
++prototype.a;
console.log(obj.a); // => 2

데이터 처리를 위한 라이브러리


Immutable.js


Immutable은 완전히 변하기 쉬운 데이터베이스에 있습니다.있다이것을 사용하면 무의식적인 변경으로 인한 오류를 방지할 수 있다.
import * as Immutable from "immutable";

const map1 = Immutable.Map({ a: 1, b: 2 });
const map2 = map1.set("c", 3);

console.assert( Immutable.is(map1, Immutable.Map({ a: 1, b: 2 })) );
console.assert( Immutable.is(map2, Immutable.Map({ a: 1, b: 2, c: 3 })) );

const list1 = Immutable.List([1, 2]);
const list2 = list1.push(3);

console.assert( Immutable.is(list1, Immutable.List([1, 2])) );
console.assert( Immutable.is(list2, Immutable.List([1, 2, 3])) );
https://immutable-js.com/

영구 데이터 구조


Immutable.js는 지속적인 데이터 구조의 기술을 사용합니다.영구 데이터 구조는 데이터를 갱신할 때 갱신 전 데이터를 유지하는 데이터 구조를 가리킨다.
https://ja.wikipedia.org/wiki/영구 데이터 구조
매번 데이터를 업데이트할 때마다 내부에서 차이를 얻고 유지하기 때문에 메모리의 사용을 억제할 수 있다.또한 두 개의 이블라토의 데이터를 비교할 때 모든 속성을 비교할 필요가 없을 수도 있다.
Directed Acyclic Graph (React.js Conf 2015 - Immutable Data and React)
다른 한편, 데이터 구조에 현저한 변화가 발생할 때는 적합하지 않다.또 수치 갱신 원가가 일반 대상보다 적지만 얻는 원가가 증가했다.

Immer


한 Immer는 객체에서 새 객체를 쉽게 작성할 수 있는 라이브러리입니다.제공된 produce는 함수를 사용하여 객체를 만들 때만 원본 객체가 변경되지 않도록 보장합니다.새로 생성된 객체는 동결됩니다.
import produce from "immer";

const obj1 = { a: 1, b: 2 };
const obj2 = produce(obj1, (draft) => {
  draft.c = 3;
});

console.log(obj1); // => { a: 1, b: 2 }
console.log(obj2); // => { a: 1, b: 2, c: 3 }

console.assert( !Object.isFrozen(obj1) );
console.assert( Object.isFrozen(obj2) );

const arr1 = [1, 2];
const arr2 = produce(arr1, (draft) => {
  draft.push(3);
});

console.log(arr1); // => [1. 2]
console.log(arr2); // => [1, 2, 3]

console.assert( !Object.isFrozen(arr1) );
console.assert( Object.isFrozen(arr2) );
https://immerjs.github.io/immer/

Immutable.js와 Immer의 비교


두 개 모두 이블라토가 데이터를 처리하는 프로그램 라이브러리로 유명하지만 특성은 크게 다르다.자세한 내용은 uhyo의 보도를 보십시오.
https://zenn.dev/uhyo/articles/immutable-immer

Stage 2 Record & Tuple


이 제안은 역시 Stage 2입니다.규격이 변경될 가능성이 있다.
새로운 원시 재료로'복합 원시(Commund primitive)'의 데이터형RecordTuple을 제시했다.프로그램 라이브러리에 의존하지 않고 이세계의 데이터를 완전히 처리할 수 있다.
const record1 = #{ a: 1, b: 2 };
const record2 = #{ ...record1, c: 3 };

console.assert( record1 === #{ a: 1, b: 2 } );
console.assert( record2 === #{ a: 1, b: 2, c: 3 } );

console.log(record1); // => Record { a: 1, b: 2 }
console.log(record2); // => Record { a: 1, b: 2, c: 3 }

const tuple1 = #[1, 2];
const tuple2 = tuple1.pushed(3);

console.assert( tuple1 === #[1, 2] );
console.assert( tuple2 === #[1, 2, 3] );

console.log(tuple1); // => Tuple [1, 2]
console.log(tuple2); // => Tuple [1, 2, 3]
https://github.com/tc39/proposal-record-tuple
이 제안은 임무블입니다.js와 Immer 두 가지가 가지고 있는 좋은 특징을 동시에 갖추었다.Immutable.js처럼 정음형 데이터를 완전히 구축하고 실제 엔진에 있어 영구 데이터 구조를 사용하면 계산량의 원가를 줄이고 최적화할 여지가 있다[1].또한 Immer처럼 조작이 간단하여 데이터 내용을 표시할 수 있다.

속성 제한

RecordTuple는 속성으로서 원시값만 유지할 수 있습니다.
const record = #{
  id: 1234,
  symbol: Symbol(),
  // Tuple 自体はプリミティブなので保持できる
  tags: #["foo", "bar", "baz"],
};
// throws TypeError: cannot use an object as a value in a record
const record = #{
  foo: {},
};
Record 속성의 키는 문자열뿐입니다.이것은 후술한 수치의 비교에 편리하도록 하기 위해서다.
// throws TypeError: Record may only have string as keys
const record = #{
  [Symbol()]: "foo",
};
https://github.com/tc39/proposal-record-tuple/issues/15
그리고 Tuple 같은 홀은 허용되지 않는다.
// SyntaxError: holes are disallowed by syntax
const tuple = #[1, 2, , 4];
https://github.com/tc39/proposal-record-tuple/issues/84

어떻게든 상대를 갖고 싶은 경우.


숫자나 문자열 등을 사용하여 객체에 해당하는 테이블을 작성하여 Array 또는 Record에서 객체 설정을 시뮬레이션할 수 있습니다.특히 어떤 처리 방법을 사용하면 편리해질 수 있다.하지만 이렇게 설치하면 GC에 따라 대상을 열 수 없는 문제가 있습니다.
이 문제를 해결하기 위한 제안은 Stage 2 Symbols WeakMap keys입니다.
https://github.com/tc39/proposal-symbols-as-weakmap-keys Tuple의 키WeakMap를 사용하면 Symbol에 대한 인용이 사라지면 대응하는 대상도 CG로 회수할 수 있다.
또 사용Symbol 방법과 달리 대상을 보다 직접적으로 보유WeakMap하는 방법을 도입하는 방안도 논의되고 있다.
https://github.com/tc39/proposal-record-tuple/issues/200

등가성

Box 연산자나 ===를 사용하면 데이터가 같은지 아닌지를 판단할 수 있다.
console.assert( #{ a: 1, b: #[1, 2] } === #{ a: 1, b: #[1, 2] } );
Object.is,0,-0의 처리가 논의 중이다.
console.assert( #{ a: -0 } === #{ a: +0 } );
console.assert( #[-0] === #[+0] );
console.assert( #{ a: NaN } === #{ a: NaN } );
console.assert( #[NaN] === #[NaN] );

console.assert( !Object.is(#{ a: -0 }, #{ a: +0 }) );
console.assert( !Object.is(#[-0], #[+0]) );
console.assert( Object.is(#{ a: NaN }, #{ a: NaN }) );
console.assert( Object.is(#[NaN], #[NaN]) );
https://github.com/tc39/proposal-record-tuple/issues/65

NaN 연산자

typeof를 연산자로 한 결과 증가typeof,"record".
console.log(typeof #{ a: 1 }); // => "record"
console.log(typeof #[1, 2]); // => "tuple"

JSON


일반적인 객체와 마찬가지로 "tuple" JSON 문자열을 만들 수 있습니다.
console.log(JSON.stringify(#{ a: #[1, 2, 3] })); // => '{"a":[1,2,3]}'
는 반대로 JSON 문자열JSON.stringify이나 Record를 만들 때Tuple를 사용한다.
console.log(JSON.parseImmutable('{"a":[1,2,3]}')); // => Record { a: Tuple [1, 2, 3] }

심층 구조를 가지고 있을 때의 상량


만약 심층 구조가 있다면, 전자 표로만 묘사하는 것은 매우 어렵다.이 문제를 해결한 사람은 임머입니다.
따라서
const record1 = #{
  a: #{
    b: #{
      foo: 1,
      bar: 2,
    },
    baz: 3,
  },
};
const record2 = #{
  ...record1,
  a: #{
    ...record1.a,
    b: #{
      ...record1.a.b,
      bar: 5,
    },
  },
};
확장JSON.parseImmutable 플랫폼의 제안은 Stage 1 Deep Path Properties in Record Literals입니다.
const record1 = #{
  a: #{
    b: #{
      foo: 1,
      bar: 2,
    },
    baz: 3,
  },
};
const record2 = #{
  ...record1,
  a.b.bar: 5,
};
https://github.com/tc39/proposal-deep-path-properties-for-record

Tuple의 특징 방법

Record는 원형에서 Tuple와 같은 방법이 있지만 비파괴적이기 때문에 일부Array와 다르다.

Array

Tuple#pushed의 끝에 원소를 하나 이상 추가하고 새 원소Tuple를 되돌려줍니다.Tuple처럼 요소수를 얻을 수 없다.
const tuple1 = #[1, 2];
const tuple2 = tuple1.pushed(3, 4);

console.assert( tuple1 === #[1, 2] );
console.assert( tuple2 === #[1, 2, 3, 4] );

Array#push

Tuple#popped 끝에서 원소를 제거한 새 Tuple를 되돌려줍니다.Tuple와 같이 제거된 요소를 가져올 수 없습니다.
const tuple1 = #[1, 2, 3];
const tuple2 = tuple1.popped();

console.assert( tuple1 === #[1, 2, 3] );
console.assert( tuple2 === #[1, 2] );

Array#pop

Tuple#unshifted의 시작에 하나 이상의 요소를 추가하고 새 Tuple를 되돌려줍니다.Tuple처럼 요소수를 얻을 수 없다.
const tuple1 = #[1, 2];
const tuple2 = tuple1.unshifted(3, 4);

console.assert( tuple1 === #[1, 2] );
console.assert( tuple2 === #[3, 4, 1, 2] );

Array#unshift

Tuple#shifted부터 원소를 제거한 새 Tuple를 되돌려줍니다.Tuple와 같이 제거된 요소를 가져올 수 없습니다.
const tuple1 = #[1, 2, 3];
const tuple2 = tuple1.shifted();

console.assert( tuple1 === #[1, 2, 3] );
console.assert( tuple2 === #[2, 3] );

Array#shift


지정한 색인 값을 업데이트한 새 값Tuple#with을 되돌려줍니다.인덱스에 음수 또는 Tuple 이상의 값을 입력한 경우Tuple#length.
const tuple1 = #[1, 2, 3];
const tuple2 = tuple1.with(1, 5);

console.assert( tuple1 === #[1, 2, 3] );
console.assert( tuple2 === #[1, 5, 3] );

RangeError


지정한 인덱스에서 임의의 원소를 삭제하고 원소를 추가한 새로운 원소Tuple#spliced를 되돌려줍니다.Tuple와 같이 제거된 요소를 가져올 수 없습니다.
const tuple1 = #[1, 2, 3, 4];
const tuple2 = tuple1.spliced(1, 2);
const tuple3 = tuple1.spliced(1, 2, 5, 6);

console.assert( tuple1 === #[1, 2, 3, 4] );
console.assert( tuple2 === #[1, 4] );
console.assert( tuple3 === #[1, 5, 6, 4] );

Array#splice

Tuple#sorted를 정렬하고 새 Tuple를 되돌려줍니다.매개 변수를 생략하면 Tuple 와 같이 문자열을 통해 비교합니다.
const tuple1 = #[10, 20, 1, 2];
const tuple2 = tuple1.sorted();
const tuple3 = tuple1.sorted((a, b) => a - b);

console.assert( tuple1 === #[10, 20, 1, 2] );
console.assert( tuple2 === #[1, 10, 2, 20] );
console.assert( tuple3 === #[1, 2, 10, 20] );

Array

Tuple#reversed의 원소가 반전된 새로운 원소Tuple.
const tuple1 = #[1, 2, 3];
const tuple2 = tuple1.reversed();

console.assert( tuple1 === #[1, 2, 3] );
console.assert( tuple2 === #[3, 2, 1] );

Tuple과 Arry에 대해서도 비파괴적인 방법이 있어요.


수락TypedArray한 방법TupleArray에도 비파괴적인 방법을 추가하자는 제안은 Stage 2 Change Aray by Copy입니다.
https://github.com/tc39/proposal-change-array-by-copy
이 제안은 새로 작성된TypedArrayArray로 원래TypedArray의 영향을 받아야 하는데 그렇지 않다.상세한 상황은 이전에 쓴 보도를 보십시오.
https://zenn.dev/petamoriken/articles/d413ce090e40bb

매듭을 짓다


이번에는 이블라토 데이터를 처리할 수 있는 새로운 원시재료@@speciesRecord를 소개했다.
이 제안은 Stage 3 Temporal 과 마찬가지로 문서와 요리책을 제공하는 데 상당히 힘이 있다.
[2]
https://tc39.es/proposal-record-tuple/tutorial/
또 실제 테스트 코드의 플레이 그라운드도 준비했다.
https://tc39.es/proposal-record-tuple/cookbook/
특히 리액트 등에서는 컨디션 관리 편의를 위해 활용이 기대된다.ECMAScript를 추구하는 제안이 즐거우실 텐데 여러분도 괜찮으시면 잘 부탁드립니다.
각주
최적화는 규격에 포함되지 않고 특별한 제한도 없다.엔진을 설치하는 데 있어서 자유롭게 선택할 수 있다.https://rickbutton.github.io/record-tuple-playground/
Temporal의↩︎,설명서(일본어)쿠크 서

좋은 웹페이지 즐겨찾기