const proposal = ##{ id: 1234, title: "Record & Tuple proposal", contents: `...`, // tuples are primitive types so you can put them in records: keywords: ##["ecma", "tc39", "proposal", "record", "tuple"], };
// Accessing keys like you would with objects! console.log(proposal.title); // Record & Tuple proposal console.log(proposal.keywords[1]); // tc39
// Spread like objects! const proposal2 = ##{ ...proposal, title: "Stage 2: Record & Tuple", }; console.log(proposal2.title); // Stage 2: Record & Tuple console.log(proposal2.keywords[1]); // tc39
// Object functions work on Records: console.log(Object.keys(proposal)); // ["contents", "id", "keywords", "title"]
// or use the .with() shorthand for the same result: const correctedMeasures2 = measures.with(3, -1); console.log(correctedMeasures2[0]); // 42 console.log(correctedMeasures2[3]); // -1
// Tuples support methods similar to Arrays console.log(correctedMeasures2.map(x => x + 1)); // ##[43, 13, 68, 0]
在函数内处理时,拿到一个数组或 Tuple 并没有什么需要特别注意的区别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
const ship1 = ##[1, 2]; // ship2 is an array: const ship2 = [-1, 3];
functionmove(start, deltaX, deltaY) { // we always return a tuple after moving return ##[ start[0] + deltaX, start[1] + deltaY, ]; }
const ship1Moved = move(ship1, 1, 0); // passing an array to move() still works: const ship2Moved = move(ship2, 3, -1);
console.log(ship1Moved === ship2Moved); // true // ship1 and ship2 have the same coordinates after moving
由于 Record 内不能定义普通对象(比如定义为 ## 标记的不可变对象),如果非要使用普通对象,只能包裹在 Box 里,并且在获取值时需要调用 .unbox() 拆箱,并且就算修改了对象值,在 Record 或 Tuple 层面也不会认为发生了变化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
const myObject = { x: 2 };
const record = ##{ name: "rec", data: Box(myObject) };
console.log(record.data.unbox().x); // 2
// The box contents are classic mutable objects: record.data.unbox().x = 3; console.log(myObject.x); // 3
另外不能在 Records & Tuples 内使用任何普通对象或 new 对象实例,除非已经用转化为了普通对象:
1 2 3 4 5 6 7 8 9 10 11 12 13
const instance = newMyClass(); const constContainer = ##{ instance: instance }; // TypeError: Record literals may only contain primitives, Records and Tuples
const tuple = ##[1, 2, 3];
tuple.map(x =>newMyClass(x)); // TypeError: Callback to Tuple.prototype.map may only return primitives, Records or Tuples
// The following should work: Array.from(tuple).map(x =>newMyClass(x))
const record = Record({ a: 1, b: 2, c: 3 }); const record2 = Record.fromEntries([##["a", 1], ##["b", 2], ##["c", 3]]); // note that an iterable will also work const tuple = Tuple(...[1, 2, 3]); const tuple2 = Tuple.from([1, 2, 3]); // note that an iterable will also work
assert(record === ##{ a: 1, b: 2, c: 3 }); assert(tuple === ##[1, 2, 3]); Record.from({ a: {} }); // TypeError: Can't convert Object with a non-const value to Record Tuple.from([{}, {} , {}]); // TypeError: Can't convert Iterable with a non-const value to Tuple
此方法不支持嵌套,因为标准 API 仅考虑一层,递归一般交给业务或库函数实现,就像 Object.assign 一样。
Record 与 Tuple 也都是可迭代的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
const tuple = ##[1, 2];
// output is: // 1 // 2 for (const o of tuple) { console.log(o); }
const record = ##{ a: 1, b: 2 };
// TypeError: record is not iterable for (const o of record) { console.log(o); }
// Object.entries can be used to iterate over Records, just like for Objects // output is: // a // b for (const [key, value] ofObject.entries(record)) { console.log(key) }