ผู้เขียน:
แชมเปี้ยนส์:
ที่ปรึกษา
เวที: 2
Record
และรองรับห้องสมุดมาตรฐาน Tuple
ข้อเสนอนี้แนะนำโครงสร้างข้อมูลใหม่ที่ไม่เปลี่ยนรูปแบบใหม่สองแบบไปยัง JavaScript:
Record
โครงสร้างคล้ายวัตถุที่เปลี่ยนไม่ได้อย่างลึกซึ้ง #{ x: 1, y: 2 }
Tuple
โครงสร้างคล้ายอาร์เรย์ที่เปลี่ยนไม่ได้อย่างลึกซึ้ง #[1, 2, 3, 4]
บันทึกและ tuples สามารถมีเฉพาะเบื้องต้นและบันทึกอื่น ๆ และ tuples คุณสามารถนึกถึงบันทึกและ tuples เป็น "สารประกอบดั้งเดิม" โดยการขึ้นอยู่กับดั้งเดิมอย่างละเอียดไม่ใช่วัตถุบันทึกและ tuples นั้นไม่เปลี่ยนรูปอย่างลึกซึ้ง
บันทึกและ tuples สนับสนุนสำนวนที่สะดวกสบายสำหรับการก่อสร้างการจัดการและการใช้งานคล้ายกับการทำงานกับวัตถุและอาร์เรย์ พวกเขาถูกเปรียบเทียบอย่างลึกซึ้งโดยเนื้อหาของพวกเขามากกว่าโดยตัวตนของพวกเขา
เครื่องยนต์ JavaScript อาจดำเนินการปรับให้เหมาะสมบางอย่างเกี่ยวกับการก่อสร้างการจัดการและการเปรียบเทียบบันทึกและ tuples ซึ่งคล้ายกับวิธีที่สตริงมักจะนำไปใช้ในเครื่องยนต์ JS (ควรเข้าใจว่าการเพิ่มประสิทธิภาพเหล่านี้ไม่ได้รับประกัน)
บันทึกและ tuples มุ่งหวังที่จะใช้งานและเข้าใจด้วย supersets ระบบภายนอกเช่น typescript หรือ flow
วันนี้ห้องสมุด Userland ใช้แนวคิดที่คล้ายกันเช่น immutable.js นอกจากนี้ยังมีการพยายามข้อเสนอก่อนหน้านี้ แต่ถูกทอดทิ้งเนื่องจากความซับซ้อนของข้อเสนอและขาดกรณีการใช้งานที่เพียงพอ
ข้อเสนอใหม่นี้ยังคงได้รับแรงบันดาลใจจากข้อเสนอก่อนหน้านี้ แต่แนะนำการเปลี่ยนแปลงที่สำคัญบางอย่าง: บันทึกและ tuples ตอนนี้เปลี่ยนไม่ได้อย่างลึกซึ้ง สถานที่ให้บริการนี้มีพื้นฐานจากการสังเกตว่าในโครงการขนาดใหญ่ความเสี่ยงของการผสมโครงสร้างข้อมูลที่ไม่เปลี่ยนแปลงและไม่แน่นอนจะเพิ่มขึ้นเมื่อปริมาณข้อมูลที่ถูกเก็บไว้และผ่านไปรอบ ๆ . สิ่งนี้สามารถแนะนำข้อบกพร่องที่หายาก
ในฐานะที่เป็นโครงสร้างข้อมูลในตัวและไม่เปลี่ยนรูปแบบอย่างลึกล้ำข้อเสนอนี้ยังมีข้อได้เปรียบในการใช้งานเล็กน้อยเมื่อเทียบกับไลบรารี Userland:
Immer เป็นวิธีการที่โดดเด่นในการสร้างโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปและกำหนดรูปแบบสำหรับการจัดการผ่านผู้ผลิตและตัวลด มันไม่ได้ให้ชนิดข้อมูลที่ไม่เปลี่ยนรูปอย่างไรก็ตามเนื่องจากมันสร้างวัตถุแช่แข็ง รูปแบบเดียวกันนี้สามารถปรับให้เข้ากับโครงสร้างที่กำหนดไว้ในข้อเสนอนี้นอกเหนือจากวัตถุแช่แข็ง
ความเท่าเทียมกันอย่างลึกซึ้งตามที่กำหนดไว้ในไลบรารีผู้ใช้อาจแตกต่างกันอย่างมีนัยสำคัญส่วนหนึ่งเนื่องจากการอ้างอิงที่เป็นไปได้กับวัตถุที่ไม่แน่นอน โดยการวาดเส้นแข็งเกี่ยวกับการมีส่วนร่วมอย่างลึกซึ้งบันทึกและ tuples และเรียกคืนผ่านโครงสร้างทั้งหมดข้อเสนอนี้กำหนดความหมายที่เรียบง่ายและเป็นเอกภาพสำหรับการเปรียบเทียบ
Record
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"]
เปิดในสนามเด็กเล่น
ฟังก์ชั่นสามารถจัดการระเบียนและวัตถุโดยทั่วไปในลักษณะเดียวกัน:
const ship1 = # { x : 1 , y : 2 } ;
// ship2 is an ordinary object:
const ship2 = { x : - 1 , y : 3 } ;
function move ( start , deltaX , deltaY ) {
// we always return a record after moving
return # {
x : start . x + deltaX ,
y : start . y + deltaY ,
} ;
}
const ship1Moved = move ( ship1 , 1 , 0 ) ;
// passing an ordinary object to move() still works:
const ship2Moved = move ( ship2 , 3 , - 1 ) ;
console . log ( ship1Moved === ship2Moved ) ; // true
// ship1 and ship2 have the same coordinates after moving
เปิดในสนามเด็กเล่น
ดูตัวอย่างเพิ่มเติมที่นี่
Tuple
const measures = # [ 42 , 12 , 67 , "measure error: foo happened" ] ;
// Accessing indices like you would with arrays!
console . log ( measures [ 0 ] ) ; // 42
console . log ( measures [ 3 ] ) ; // measure error: foo happened
// Slice and spread like arrays!
const correctedMeasures = # [
... measures . slice ( 0 , measures . length - 1 ) ,
- 1
] ;
console . log ( correctedMeasures [ 0 ] ) ; // 42
console . log ( correctedMeasures [ 3 ] ) ; // -1
// 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]
เปิดในสนามเด็กเล่น
ในทำนองเดียวกันกับบันทึกเราสามารถรักษา tuples เป็นอาร์เรย์ได้:
const ship1 = # [ 1 , 2 ] ;
// ship2 is an array:
const ship2 = [ - 1 , 3 ] ;
function move ( 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
เปิดในสนามเด็กเล่น
ดูตัวอย่างเพิ่มเติมที่นี่
ตามที่ระบุไว้ก่อนที่บันทึก & tuple จะเปลี่ยนไม่ได้อย่างลึกซึ้ง: การพยายามแทรกวัตถุในนั้นจะส่งผลให้ typeerror:
const instance = new MyClass ( ) ;
const constContainer = # {
instance : instance
} ;
// TypeError: Record literals may only contain primitives, Records and Tuples
const tuple = # [ 1 , 2 , 3 ] ;
tuple . map ( x => new MyClass ( x ) ) ;
// TypeError: Callback to Tuple.prototype.map may only return primitives, Records or Tuples
// The following should work:
Array . from ( tuple ) . map ( x => new MyClass ( x ) )
สิ่งนี้กำหนดไวยากรณ์ชิ้นใหม่ที่ถูกเพิ่มเข้ากับภาษาด้วยข้อเสนอนี้
เรากำหนดบันทึกหรือนิพจน์ tuple โดยใช้ #
modifier ด้านหน้าของวัตถุปกติหรือนิพจน์อาร์เรย์
# { }
# { a : 1 , b : 2 }
# { a : 1 , b : # [ 2 , 3 , # { c : 4 } ] }
# [ ]
# [ 1 , 2 ]
# [ 1 , 2 , # { a : 3 } ]
มีการป้องกันหลุมในไวยากรณ์ซึ่งแตกต่างจากอาร์เรย์ซึ่งอนุญาตให้มีรู ดูปัญหา #84 สำหรับการสนทนาเพิ่มเติม
const x = # [ , ] ; // SyntaxError, holes are disallowed by syntax
การใช้ตัวระบุ __proto__
เป็นคุณสมบัติถูกป้องกันในไวยากรณ์ ดูปัญหา #46 สำหรับการสนทนาเพิ่มเติม
const x = # { __proto__ : foo } ; // SyntaxError, __proto__ identifier prevented by syntax
const y = # { [ "__proto__" ] : foo } ; // valid, creates a record with a "__proto__" property.
วิธีการกระชับไม่ได้รับอนุญาตในไวยากรณ์บันทึก
# { method ( ) { } } // SyntaxError
ระเบียนอาจมีคีย์สตริงเท่านั้นไม่ใช่คีย์สัญลักษณ์เนื่องจากปัญหาที่อธิบายไว้ใน #15 การสร้างบันทึกด้วยคีย์สัญลักษณ์คือ TypeError
const record = # { [ Symbol ( ) ] : # { } } ;
// TypeError: Record may only have string as keys
บันทึกและ tuples อาจมีเฉพาะเบื้องต้นและบันทึกอื่น ๆ และ tuples พยายามที่จะสร้าง Record
หรือ Tuple
ที่มี Object
( null
ไม่ใช่วัตถุ) หรือ Function
พ่น TypeError
const obj = { } ;
const record = # { prop : obj } ; // TypeError: Record may only contain primitive values
ความเท่าเทียมกันของระเบียนและ tuples ทำงานเหมือนของประเภท primitive js อื่น ๆ เช่นบูลีนและค่าสตริงเปรียบเทียบโดยเนื้อหาไม่ใช่ตัวตน:
assert ( # { a : 1 } === # { a : 1 } ) ;
assert ( # [ 1 , 2 ] === # [ 1 , 2 ] ) ;
สิ่งนี้แตกต่างจากความเท่าเทียมกันสำหรับวัตถุ JS: การเปรียบเทียบวัตถุจะสังเกตว่าแต่ละวัตถุมีความแตกต่าง:
assert ( { a : 1 } !== { a : 1 } ) ;
assert ( Object ( # { a : 1 } ) !== Object ( # { a : 1 } ) ) ;
assert ( Object ( # [ 1 , 2 ] ) !== Object ( # [ 1 , 2 ] ) ) ;
ลำดับการแทรกของคีย์บันทึกไม่ส่งผลกระทบต่อความเท่าเทียมกันของบันทึกเนื่องจากไม่มีวิธีใดที่จะสังเกตการสั่งซื้อดั้งเดิมของคีย์ตามที่เรียงลำดับโดยปริยาย:
assert ( # { a : 1 , b : 2 } === # { b : 2 , a : 1 } ) ;
Object . keys ( # { a : 1 , b : 2 } ) // ["a", "b"]
Object . keys ( # { b : 2 , a : 1 } ) // ["a", "b"]
หากโครงสร้างและเนื้อหาของพวกเขาเหมือนกันอย่างลึกซึ้งจากนั้นค่า Record
และค่า Tuple
ถือว่าเท่ากันตามการดำเนินการเท่าเทียมกันทั้งหมด: Object.is
, ==
, ===
และอัลกอริทึม samevalueZero ภายใน (ใช้สำหรับการเปรียบเทียบคีย์ของแผนที่และชุด) . พวกเขาแตกต่างกันในแง่ของวิธีการรักษา -0
:
Object.is
ปฏิบัติ -0
และ 0
เป็นไม่เท่ากัน==
, ===
และ samevaluezero treat -0
กับ 0
เท่ากับเท่ากัน โปรดทราบว่า ==
และ ===
นั้นมีค่าโดยตรงมากขึ้นเกี่ยวกับค่าชนิดอื่น ๆ ที่ซ้อนกันในบันทึกและ tuples-กลับ true
ถ้าเนื้อหานั้นเหมือนกัน (ยกเว้น 0
/ -0
) ความตรงนี้มีผลกระทบต่อ NaN
เช่นเดียวกับการเปรียบเทียบข้ามประเภท ดูตัวอย่างด้านล่าง
ดูการสนทนาเพิ่มเติมใน #65
assert ( # { a : 1 } === # { a : 1 } ) ;
assert ( # [ 1 ] === # [ 1 ] ) ;
assert ( # { a : - 0 } === # { a : + 0 } ) ;
assert ( # [ - 0 ] === # [ + 0 ] ) ;
assert ( # { a : NaN } === # { a : NaN } ) ;
assert ( # [ NaN ] === # [ NaN ] ) ;
assert ( # { a : - 0 } == # { a : + 0 } ) ;
assert ( # [ - 0 ] == # [ + 0 ] ) ;
assert ( # { a : NaN } == # { a : NaN } ) ;
assert ( # [ NaN ] == # [ NaN ] ) ;
assert ( # [ 1 ] != # [ "1" ] ) ;
assert ( ! Object . is ( # { a : - 0 } , # { a : + 0 } ) ) ;
assert ( ! Object . is ( # [ - 0 ] , # [ + 0 ] ) ) ;
assert ( Object . is ( # { a : NaN } , # { a : NaN } ) ) ;
assert ( Object . is ( # [ NaN ] , # [ NaN ] ) ) ;
// Map keys are compared with the SameValueZero algorithm
assert ( new Map ( ) . set ( # { a : 1 } , true ) . get ( # { a : 1 } ) ) ;
assert ( new Map ( ) . set ( # [ 1 ] , true ) . get ( # [ 1 ] ) ) ;
assert ( new Map ( ) . set ( # [ - 0 ] , true ) . get ( # [ 0 ] ) ) ;
Record
และ Tuple
โดยทั่วไปคุณสามารถรักษาบันทึกเช่นวัตถุ ตัวอย่างเช่น Object
Namespace และ in
Operator ทำงานกับ Records
const keysArr = Object . keys ( # { a : 1 , b : 2 } ) ; // returns the array ["a", "b"]
assert ( keysArr [ 0 ] === "a" ) ;
assert ( keysArr [ 1 ] === "b" ) ;
assert ( keysArr !== # [ "a" , "b" ] ) ;
assert ( "a" in # { a : 1 , b : 2 } ) ;
Record
และวัตถุห่อหุ้ม Tuple
โดยทั่วไปแล้วนักพัฒนา JS จะไม่ต้องคิดเกี่ยวกับวัตถุ Record
และ Tuple
wrapper แต่พวกเขาเป็นส่วนสำคัญของการบันทึกและ tuples ทำงาน "ภายใต้ประทุน" ในข้อกำหนด JavaScript
การเข้าถึงบันทึกหรือ .
ผ่าน หรือ []
ตามความหมาย GetValue
ทั่วไปซึ่งจะแปลงเป็นอินสแตนซ์ของประเภท wrapper ที่สอดคล้องกันโดยปริยาย นอกจากนี้คุณยังสามารถทำการแปลงได้อย่างชัดเจนผ่าน Object()
:
Object(record)
สร้างวัตถุ wrapper บันทึกObject(tuple)
สร้างวัตถุ wrapper tuple (ใครจะจินตนาการได้ว่า new Record
หรือ new Tuple
สามารถสร้าง wrappers เหล่านี้เช่น new Number
และ new String
ทำ แต่บันทึกและ tuples ติดตามการประชุมใหม่ที่กำหนดโดย Symbol และ Bigint ส่งเสริมให้โปรแกรมเมอร์ใช้)
วัตถุบันทึกและ tuple wrapper มีคุณสมบัติทั้งหมดของตัวเองพร้อมกับแอตทริบิวต์ writable: false, enumerable: true, configurable: false
วัตถุห่อหุ้มไม่สามารถขยายได้ ทั้งหมดรวมกันพวกเขาทำหน้าที่เป็นวัตถุแช่แข็ง สิ่งนี้แตกต่างจากวัตถุห่อหุ้มที่มีอยู่ใน JavaScript แต่จำเป็นต้องให้ข้อผิดพลาดที่คุณคาดหวังจากการจัดการทั่วไปในบันทึกและ tuples
อินสแตนซ์ของ Record
มีคีย์และค่าเดียวกันกับค่า record
พื้นฐาน __proto__
ของวัตถุ wrapper บันทึกเหล่านี้แต่ละชิ้นคือ null
(การสนทนา: #71)
อินสแตนซ์ของ Tuple
มีคีย์ที่เป็นจำนวนเต็มที่สอดคล้องกับแต่ละดัชนีในค่า tuple
พื้นฐาน ค่าสำหรับแต่ละปุ่มเหล่านี้คือค่าที่สอดคล้องกันใน tuple
ดั้งเดิม นอกจากนี้ยังมีคีย์ length
ที่ไม่สามารถใช้งานได้ โดยรวมแล้วคุณสมบัติเหล่านี้ตรงกับวัตถุ String
wrapper นั่นคือ Object.getOwnPropertyDescriptors(Object(#["a", "b"]))
และ Object.getOwnPropertyDescriptors(Object("ab"))
แต่ละรายการจะส่งคืนวัตถุที่มีลักษณะเช่นนี้:
{
"0" : {
"value" : " a " ,
"writable" : false ,
"enumerable" : true ,
"configurable" : false
},
"1" : {
"value" : " b " ,
"writable" : false ,
"enumerable" : true ,
"configurable" : false
},
"length" : {
"value" : 2 ,
"writable" : false ,
"enumerable" : false ,
"configurable" : false
}
}
__proto__
ของวัตถุห่อหุ้ม tuple คือ Tuple.prototype
โปรดทราบว่าหากคุณกำลังทำงานกับวัตถุ Global Global JavaScript ที่แตกต่างกัน ("Realms") Tuple.prototype
จะถูกเลือกตามอาณาจักรปัจจุบันเมื่อทำการแปลงวัตถุนั้นคล้ายกับวิธี .prototype
ไม่แนบกับค่า tuple ของตัวเอง Tuple.prototype
มีวิธีการต่าง ๆ ในนั้นคล้ายกับอาร์เรย์
เพื่อความสมบูรณ์การทำดัชนีตัวเลขนอกขอบเขตของ Tuples จะส่งคืน undefined
แทนที่จะส่งต่อผ่านห่วงโซ่ต้นแบบเช่นเดียวกับ TypedArrays การค้นหาคีย์ทรัพย์สินที่ไม่ใช่ตัวเลขส่งต่อไปยัง Tuple.prototype
ซึ่งเป็นสิ่งสำคัญในการค้นหาวิธีการคล้ายอาร์เรย์
Record
และรองรับห้องสมุดมาตรฐาน Tuple
ค่า Tuple
มีฟังก์ชั่นที่คล้ายคลึงกับ Array
ในวงกว้าง ในทำนองเดียวกันค่า Record
ได้รับการสนับสนุนโดยวิธีการคงที่ของ Object
ที่แตกต่างกัน
assert . deepEqual ( Object . keys ( # { a : 1 , b : 2 } ) , [ "a" , "b" ] ) ;
assert ( # [ 1 , 2 , 3 ] . map ( x => x * 2 ) , # [ 2 , 4 , 6 ] ) ;
ดูภาคผนวกเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับเนมสเปซ Record
& Tuple
คุณสามารถแปลงโครงสร้างโดยใช้ Record()
, Tuple()
(ด้วยตัวดำเนินการสเปรด), Record.fromEntries()
หรือ Tuple.from()
::
const record = Record ( { a : 1 , b : 2 , c : 3 } ) ;
const record2 = Record . fromEntries ( [ [ "a" , 1 ] , # [ "b" , 2 ] , { 0 : 'c' , 1 : 3 } ] ) ; // note that any iterable of entries will 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 ( { 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
โปรดทราบว่า Record()
, Tuple()
, Record.fromEntries()
และ Tuple.from()
คาดว่าคอลเลกชันที่ประกอบด้วยบันทึก tuples หรือ primitives อื่น ๆ (เช่นตัวเลขสตริง ฯลฯ ) การอ้างอิงวัตถุที่ซ้อนกันจะทำให้ TypeError ขึ้นอยู่กับผู้โทรที่จะแปลงโครงสร้างภายในไม่ว่าจะด้วยวิธีใดก็ตามที่เหมาะสมสำหรับแอปพลิเคชัน
หมายเหตุ : ข้อเสนอร่างปัจจุบันไม่มีการแปลงแบบเรียกซ้ำเป็นประจำมีเพียงข้อตื้นเท่านั้น ดูการสนทนาใน #122
เช่นเดียวกับอาร์เรย์ tuples เป็นสิ่งที่ซ้ำได้
const tuple = # [ 1 , 2 ] ;
// output is:
// 1
// 2
for ( const o of tuple ) { console . log ( o ) ; }
ในทำนองเดียวกันกับวัตถุระเบียนจะมีการติดตั้งร่วมกับ API เช่น Object.entries
เท่านั้น
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 ] of Object . entries ( record ) ) { console . log ( key ) }
JSON.stringify(record)
เทียบเท่ากับการเรียก JSON.stringify
บนวัตถุที่เกิดจากการแปลงบันทึกซ้ำเป็นวัตถุที่ไม่มีระเบียนหรือ tuplesJSON.stringify(tuple)
เทียบเท่ากับการโทรหา JSON.stringify
ในอาร์เรย์ที่เกิดจากการแปลง tuple ซ้ำเป็นอาร์เรย์ที่ไม่มีบันทึกหรือ tuples JSON . stringify ( # { a : # [ 1 , 2 , 3 ] } ) ; // '{"a":[1,2,3]}'
JSON . stringify ( # [ true , # { a : # [ 1 , 2 , 3 ] } ] ) ; // '[true,{"a":[1,2,3]}]'
โปรดดู https://github.com/tc39/proposal-json-parseimmutable
Tuple.prototype
Tuple
รองรับวิธีการอินสแตนซ์คล้ายกับอาร์เรย์ที่มีการเปลี่ยนแปลงเล็กน้อย:
Tuple.prototype.push
Tuple.prototype.withAt
ภาคผนวกมีคำอธิบายเต็มรูปแบบของต้นแบบของ Tuple
typeof
typeof
ระบุบันทึกและ tuples เป็นประเภทที่แตกต่าง:
assert ( typeof # { a : 1 } === "record" ) ;
assert ( typeof # [ 1 , 2 ] === "tuple" ) ;
Map
| Set
| WeakMap
WeakSet
} เป็นไปได้ที่จะใช้ Record
หรือ Tuple
เป็นคีย์ใน Map
และเป็นค่าใน Set
เมื่อใช้ Record
หรือ Tuple
ที่นี่พวกเขาจะถูกเปรียบเทียบตามค่า
เป็นไปไม่ได้ที่จะใช้ Record
หรือ Tuple
เป็นกุญแจสำคัญใน WeakMap
หรือเป็นค่าใน WeakSet
เนื่องจาก Records
และ Tuple
s ไม่ใช่ Objects
และอายุการใช้งานของพวกเขาไม่สามารถสังเกตได้
const record1 = # { a : 1 , b : 2 } ;
const record2 = # { a : 1 , b : 2 } ;
const map = new Map ( ) ;
map . set ( record1 , true ) ;
assert ( map . get ( record2 ) ) ;
const record1 = # { a : 1 , b : 2 } ;
const record2 = # { a : 1 , b : 2 } ;
const set = new Set ( ) ;
set . add ( record1 ) ;
set . add ( record2 ) ;
assert ( set . size === 1 ) ;
const record = # { a : 1 , b : 2 } ;
const weakMap = new WeakMap ( ) ;
// TypeError: Can't use a Record as the key in a WeakMap
weakMap . set ( record , true ) ;
const record = # { a : 1 , b : 2 } ;
const weakSet = new WeakSet ( ) ;
// TypeError: Can't add a Record to a WeakSet
weakSet . add ( record ) ;
ประโยชน์หลักอย่างหนึ่งของบันทึกและข้อเสนอ tuples คือพวกเขาถูกเปรียบเทียบโดยเนื้อหาของพวกเขาไม่ใช่ตัวตนของพวกเขา ในเวลาเดียวกัน ===
ใน JavaScript บนวัตถุมีความหมายที่ชัดเจนและสอดคล้องกันมาก: เพื่อเปรียบเทียบวัตถุโดยตัวตน การสร้างบันทึกและ tuples primitives ช่วยให้การเปรียบเทียบตามค่าของพวกเขา
ในระดับสูงความแตกต่างของวัตถุ/ดั้งเดิมจะช่วยสร้างเส้นแบ่งอย่างหนักระหว่างโลกที่ไม่เปลี่ยนรูปแบบที่ไม่มีบริบทและโลกของวัตถุที่ไม่แน่นอนอยู่เหนือมัน การแบ่งหมวดหมู่นี้ทำให้การออกแบบและแบบจำลองทางจิตชัดเจนขึ้น
อีกทางเลือกหนึ่งในการใช้บันทึกและ tuple เป็นดั้งเดิมคือการใช้ตัวดำเนินการมากเกินไปเพื่อให้ได้ผลลัพธ์ที่คล้ายกันโดยการใช้ตัวดำเนินการนามธรรมความเท่าเทียมกันมากเกินไป ( ==
) ที่เปรียบเทียบวัตถุอย่างลึกซึ้ง แม้ว่าจะเป็นไปได้ แต่ก็ไม่เป็นไปตามกรณีการใช้งานเต็มรูปแบบเนื่องจากผู้ให้บริการโอเวอร์โหลดไม่ได้ให้การแทนที่สำหรับผู้ให้บริการ ===
เราต้องการให้ผู้ประกอบการที่มีความเท่าเทียมกันอย่างเข้มงวด ( ===
) เป็นการตรวจสอบที่เชื่อถือได้ของ "ตัวตน" สำหรับวัตถุและ "ค่าที่สังเกตได้" (Modulo -0/+0/NAN) สำหรับประเภทดั้งเดิม
อีกทางเลือกหนึ่งคือการดำเนินการสิ่งที่เรียกว่า การฝึกงาน : เราติดตามการบันทึกทั่วโลกหรือวัตถุ tuple และถ้าเราพยายามสร้างใหม่ที่เกิดขึ้นเหมือนกับวัตถุบันทึกที่มีอยู่ตอนนี้เราอ้างอิงบันทึกที่มีอยู่นี้แทนที่จะสร้างใหม่ นี่คือสิ่งที่โพลีฟิลด์ทำ ตอนนี้เรามีค่าเท่ากับค่าและตัวตน วิธีการนี้จะสร้างปัญหาเมื่อเราขยายพฤติกรรมนั้นข้ามบริบทของจาวาสคริปต์หลายแห่งและจะไม่ให้ความไม่สามารถเปลี่ยนแปลงได้อย่างลึกล้ำและ มันช้าโดยเฉพาะอย่างยิ่ง ซึ่งจะทำให้การใช้บันทึก & tuple เป็นตัวเลือกประสิทธิภาพเชิงลบ
Record & Tuple ถูกสร้างขึ้นเพื่อทำงานร่วมกับวัตถุและอาร์เรย์ได้ดี: คุณสามารถอ่านได้ในลักษณะเดียวกับที่คุณทำกับวัตถุและอาร์เรย์ การเปลี่ยนแปลงที่สำคัญอยู่ในความไม่สามารถเปลี่ยนแปลงได้อย่างลึกซึ้งและการเปรียบเทียบโดยค่าแทนที่จะเป็นตัวตน
นักพัฒนาที่ใช้ในการจัดการวัตถุในลักษณะที่ไม่เปลี่ยนรูป (เช่นการเปลี่ยนชิ้นส่วนของสถานะ Redux) จะสามารถดำเนินการต่อไปในการจัดการแบบเดียวกับที่พวกเขาเคยทำกับวัตถุและอาร์เรย์ในครั้งนี้ด้วยการรับประกันมากขึ้น
เราจะทำการวิจัยเชิงประจักษ์ผ่านการสัมภาษณ์และการสำรวจเพื่อหาว่าสิ่งนี้ได้ผลหรือไม่ตามที่เราคิด
.get()
/ .set()
วิธีการเช่น immutable.js?หากเราต้องการเข้าถึงบันทึก & tuple คล้ายกับวัตถุและอาร์เรย์ตามที่อธิบายไว้ในส่วนก่อนหน้าเราไม่สามารถพึ่งพาวิธีการในการเข้าถึงนั้น การทำเช่นนั้นจะทำให้เราต้องใช้รหัสสาขาเมื่อพยายามสร้างฟังก์ชั่น "ทั่วไป" สามารถใช้วัตถุ/อาร์เรย์/บันทึก/tuples
นี่คือฟังก์ชั่นตัวอย่างที่มีการสนับสนุนสำหรับบันทึก js imutable.js และวัตถุธรรมดา:
const ProfileRecord = Immutable . Record ( {
name : "Anonymous" ,
githubHandle : null ,
} ) ;
const profileObject = {
name : "Rick Button" ,
githubHandle : "rickbutton" ,
} ;
const profileRecord = ProfileRecord ( {
name : "Robin Ricard" ,
githubHandle : "rricard" ,
} ) ;
function getGithubUrl ( profile ) {
if ( Immutable . Record . isRecord ( profile ) ) {
return `https://github.com/ ${
profile . get ( "githubHandle" )
} ` ;
}
return `https://github.com/ ${
profile . githubHandle
} ` ;
}
console . log ( getGithubUrl ( profileObject ) ) // https://github.com/rickbutton
console . log ( getGithubUrl ( profileRecord ) ) // https://github.com/rricard
นี่เป็นข้อผิดพลาดได้ง่ายเนื่องจากทั้งสองสาขาสามารถออกจากการซิงค์ได้อย่างง่ายดายเมื่อเวลาผ่านไป ...
นี่คือวิธีที่เราจะเขียนฟังก์ชั่นนั้นรับบันทึกจากข้อเสนอนี้และวัตถุธรรมดา:
const profileObject = {
name : "Rick Button" ,
githubHandle : "rickbutton" ,
} ;
const profileRecord = # {
name : "Robin Ricard" ,
githubHandle : "rricard" ,
} ;
function getGithubUrl ( profile ) {
return `https://github.com/ ${
profile . githubHandle
} ` ;
}
console . log ( getGithubUrl ( profileObject ) ) // https://github.com/rickbutton
console . log ( getGithubUrl ( profileRecord ) ) // https://github.com/rricard
ฟังก์ชั่นนี้รองรับทั้งวัตถุและบันทึกในเส้นทางรหัสเดียวและไม่บังคับให้ผู้บริโภคเลือกโครงสร้างข้อมูลที่จะใช้
ทำไมเราต้องสนับสนุนทั้งสองในเวลาเดียวกัน? นี่คือหลักเพื่อหลีกเลี่ยงการแยกระบบนิเวศ สมมติว่าเรากำลังใช้งานไม่เปลี่ยนรูป Js เพื่อทำการจัดการสถานะของเรา แต่เราจำเป็นต้องป้อนสถานะของเราไปยังห้องสมุดภายนอกสองสามแห่งที่ไม่สนับสนุน:
state . jobResult = Immutable . fromJS (
ExternalLib . processJob (
state . jobDescription . toJS ( )
)
) ;
ทั้ง toJS()
และ fromJS()
สามารถจบลงด้วยการดำเนินงานที่มีราคาแพงมากขึ้นอยู่กับขนาดของโครงสร้างย่อย การแยกระบบนิเวศหมายถึงการแปลงที่ในทางกลับกันหมายถึงปัญหาประสิทธิภาพที่เป็นไปได้
ไวยากรณ์ที่เสนอจะช่วยปรับปรุงการยศาสตร์ของการใช้ Record
และ Tuple
ในรหัสอย่างมีนัยสำคัญ ตัวอย่างเช่น:
// with the proposed syntax
const record = # {
a : # {
foo : "string" ,
} ,
b : # {
bar : 123 ,
} ,
c : # {
baz : # {
hello : # [
1 ,
2 ,
3 ,
] ,
} ,
} ,
} ;
// with only the Record/Tuple globals
const record = Record ( {
a : Record ( {
foo : "string" ,
} ) ,
b : Record ( {
bar : 123 ,
} ) ,
c : Record ( {
baz : Record ( {
hello : Tuple (
1 ,
2 ,
3 ,
) ,
} ) ,
} ) ,
} ) ;
ไวยากรณ์ที่เสนอนั้นมีจุดประสงค์เพื่อให้ง่ายขึ้นและง่ายกว่าที่จะเข้าใจเพราะมันมีความคล้ายคลึงกับไวยากรณ์สำหรับวัตถุและตัวอักษรอาร์เรย์ สิ่งนี้ใช้ประโยชน์จากความคุ้นเคยที่มีอยู่ของผู้ใช้กับวัตถุและอาร์เรย์ นอกจากนี้ตัวอย่างที่สองแนะนำตัวอักษรวัตถุชั่วคราวเพิ่มเติมซึ่งเพิ่มความซับซ้อนของการแสดงออก
การใช้คำหลักเป็นคำนำหน้าไปยังไวยากรณ์ของวัตถุ/อาร์เรย์มาตรฐานแสดงปัญหาเกี่ยวกับความเข้ากันได้ย้อนหลัง นอกจากนี้การใช้คำหลักที่มีอยู่ใหม่สามารถแนะนำความคลุมเครือ
Ecmascript กำหนดชุด คำหลักที่สงวนไว้ ซึ่งสามารถใช้สำหรับการขยายในอนาคตกับภาษา การกำหนดคำหลักใหม่ที่ไม่ได้สงวนไว้นั้นเป็นไปได้ในทางทฤษฎี แต่ต้องใช้ความพยายามอย่างมีนัยสำคัญในการตรวจสอบว่าคำหลักใหม่จะไม่ทำลายความเข้ากันได้ย้อนหลัง
การใช้คำหลักที่สงวนไว้ทำให้กระบวนการนี้ง่ายขึ้น แต่ไม่ใช่วิธีแก้ปัญหาที่สมบูรณ์แบบเพราะไม่มีคำหลักที่สงวนไว้ซึ่งตรงกับ "เจตนา" ของคุณลักษณะนอกเหนือจาก const
คำหลัก const
นั้นเป็นเรื่องยุ่งยากเช่นกันเพราะมันอธิบายแนวคิดที่คล้ายกัน (การอ้างอิงตัวแปรการไม่เปลี่ยนแปลง) ในขณะที่ข้อเสนอนี้ตั้งใจที่จะเพิ่มโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปใหม่ ในขณะที่การไม่เปลี่ยนรูปเป็นหัวข้อทั่วไประหว่างคุณสมบัติทั้งสองนี้มีข้อเสนอแนะของชุมชนที่สำคัญซึ่งบ่งชี้ว่าการใช้ const
ในบริบททั้งสองนั้นไม่เป็นที่พึงปรารถนา
แทนที่จะใช้คำหลัก {| |}
และ [||]
ได้รับการแนะนำให้เป็นทางเลือกที่เป็นไปได้ ปัจจุบันกลุ่มแชมป์กำลังโน้มตัวไปที่ #[]
/ #{}
แต่การสนทนายังดำเนินอยู่ใน #10
คำจำกัดความของบันทึก & tuple เป็นสารประกอบดั้งเดิมบังคับให้ทุกอย่างในบันทึก & tuple ไม่เป็นวัตถุ สิ่งนี้มาพร้อมกับข้อเสียบางอย่าง (การอ้างอิงวัตถุกลายเป็นเรื่องยาก แต่ก็ยังเป็นไปได้) แต่ยังรับประกันได้มากขึ้นเพื่อหลีกเลี่ยงข้อผิดพลาดในการเขียนโปรแกรมทั่วไป
const object = {
a : {
foo : "bar" ,
} ,
} ;
Object . freeze ( object ) ;
func ( object ) ;
// func is able to mutate object’s keys even if object is frozen
ในตัวอย่างข้างต้นเราพยายามสร้างการรับประกันความไม่สามารถเปลี่ยนแปลงได้ด้วย Object.freeze
น่าเสียดายเนื่องจากเราไม่ได้ตรึงวัตถุอย่างลึกล้ำไม่มีอะไรบอกเราว่า object.a
นั้นไม่ได้ถูกแตะต้อง ด้วยบันทึก & tuple ว่าข้อ จำกัด นั้นเป็นไปตามธรรมชาติและไม่ต้องสงสัยเลยว่าโครงสร้างนั้นไม่มีใครแตะต้อง:
const record = # {
a : # {
foo : "bar" ,
} ,
} ;
func ( record ) ;
// runtime guarantees that record is entirely unchanged
assert ( record . a . foo === "bar" ) ;
ในที่สุดความไม่สามารถเปลี่ยนแปลงได้อย่างลึกล้ำยับยั้งความจำเป็นในรูปแบบทั่วไปซึ่งประกอบด้วยวัตถุการโคลนนิ่งลึกเพื่อให้การรับประกัน:
const clonedObject = JSON . parse ( JSON . stringify ( object ) ) ;
func ( clonedObject ) ;
// now func can have side effects on clonedObject, object is untouched
// but at what cost?
assert ( object . a . foo === "bar" ) ;
โดยทั่วไปตัวดำเนินการสเปรดทำงานได้ดีสำหรับสิ่งนี้:
// Add a Record field
let rec = # { a : 1 , x : 5 }
# { ... rec , b : 2 } // #{ a: 1, b: 2, x: 5 }
// Change a Record field
# { ... rec , x : 6 } // #{ a: 1, x: 6 }
// Append to a Tuple
let tup = # [ 1 , 2 , 3 ] ;
# [ ... tup , 4 ] // #[1, 2, 3, 4]
// Prepend to a Tuple
# [ 0 , ... tup ] // #[0, 1, 2, 3]
// Prepend and append to a Tuple
# [ 0 , ... tup , 4 ] // #[0, 1, 2, 3, 4]
และถ้าคุณกำลังเปลี่ยนบางสิ่งบางอย่างใน tuple, Tuple.prototype.with
method ทำงาน:
// Change a Tuple index
let tup = # [ 1 , 2 , 3 ] ;
tup . with ( 1 , 500 ) // #[1, 500, 3]
การจัดการบางอย่างของ "เส้นทางลึก" อาจเป็นเรื่องที่น่าอึดอัดใจ สำหรับสิ่งนั้นคุณสมบัติเส้นทางลึกสำหรับข้อเสนอบันทึกเพิ่มไวยากรณ์เพิ่มเติมชวเลขเพิ่มเติมเพื่อบันทึกตัวอักษร
เรากำลังพัฒนาข้อเสนอคุณสมบัติเส้นทางลึกเป็นข้อเสนอการติดตามแยกต่างหากเพราะเราไม่เห็นว่ามันเป็นแกนหลักในการใช้ระเบียนซึ่งทำงานได้ดีอย่างอิสระ เป็นการเพิ่มวากยสัมพันธ์ซึ่งจะทำงานได้ดีกับต้นแบบเมื่อเวลาผ่านไปใน transpilers และที่ที่เรามีจุดตัดสินใจมากมายที่ไม่เกี่ยวข้องกับบันทึกและ tuples (เช่นวิธีการทำงานกับวัตถุ)
เราได้พูดคุยกับแชมเปี้ยนคอลเลกชันแบบอ่านอย่างเดียวและทั้งสองกลุ่มยอมรับว่าสิ่งเหล่านี้เป็นส่วนประกอบ:
ไม่มีใครเป็นชุดย่อยของอื่น ๆ ในแง่ของการทำงาน ที่ดีที่สุดพวกเขามีขนานกันเช่นเดียวกับแต่ละข้อเสนอนั้นขนานกับประเภทคอลเลกชันอื่น ๆ ในภาษา
ดังนั้นกลุ่มแชมป์ทั้งสองได้ตัดสินใจเพื่อให้แน่ใจว่าข้อเสนอนั้นขนาน กันไป พร้อมกัน ตัวอย่างเช่นข้อเสนอนี้เพิ่มวิธีการใหม่ Tuple.prototype.withReversed
ความคิดคือการตรวจสอบในระหว่างกระบวนการออกแบบหากลายเซ็นนี้จะสมเหตุสมผลสำหรับอาร์เรย์แบบอ่านอย่างเดียว (ถ้ามีอยู่): เราดึงวิธีการใหม่เหล่านี้ไปยังอาร์เรย์การเปลี่ยนแปลงโดยการคัดลอกข้อเสนอเพื่อให้เราสามารถหารือเกี่ยวกับ API ซึ่งสร้างรูปแบบจิตที่สอดคล้องกันและแบ่งปัน
ในร่างข้อเสนอปัจจุบันไม่มีประเภทที่ทับซ้อนกันสำหรับข้อมูลประเภทเดียวกัน แต่ข้อเสนอทั้งสองสามารถเติบโตในทิศทางเหล่านี้ในอนาคตและเรากำลังพยายามคิดสิ่งเหล่านี้ผ่านกาลเวลา ใครจะรู้ว่าบางวัน TC39 สามารถตัดสินใจที่จะเพิ่มประเภท recordmap และ recordset แบบดั้งเดิมได้เป็นชุดและแผนที่รุ่นที่ไม่เปลี่ยนรูปแบบอย่างลึกซึ้ง! และสิ่งเหล่านี้จะควบคู่ไปกับประเภทคอลเลกชันแบบอ่านอย่างเดียว
TC39 ได้พูดคุยกันมานานว่า "ประเภทค่า" ซึ่งจะเป็นการประกาศคลาสบางประเภทสำหรับประเภทดั้งเดิมเป็นเวลาหลายปีทั้งในและนอก ข้อเสนอนี้เวอร์ชันก่อนหน้านี้ได้พยายาม ข้อเสนอนี้พยายามที่จะเริ่มต้นง่ายและน้อยที่สุดโดยให้เพียงโครงสร้างหลัก ความหวังคือมันสามารถให้แบบจำลองข้อมูลสำหรับข้อเสนอในอนาคตสำหรับชั้นเรียน
ข้อเสนอนี้มีความสัมพันธ์อย่างหลวม ๆ กับชุดข้อเสนอที่กว้างขึ้นรวมถึงผู้ปฏิบัติงานมากเกินไปและขยายตัวอักษรตัวเลข: สิ่งเหล่านี้สมรู้ร่วมคิดทั้งหมดเพื่อให้วิธีการสำหรับประเภทที่ผู้ใช้กำหนดให้ทำเช่นเดียวกับ Bigint อย่างไรก็ตามความคิดคือการเพิ่มคุณสมบัติเหล่านี้หากเรากำหนดว่าพวกเขามีแรงจูงใจอย่างอิสระ
หากเรามีประเภทดั้งเดิม/ค่าที่ผู้ใช้กำหนดไว้คุณก็สามารถใช้งานได้ในคุณสมบัติในตัวเช่น CSS ที่พิมพ์ OM หรือข้อเสนอชั่วคราว อย่างไรก็ตามสิ่งนี้อยู่ไกลในอนาคตหากมันเกิดขึ้น สำหรับตอนนี้มันทำงานได้ดีในการใช้วัตถุสำหรับคุณสมบัติประเภทนี้
แม้ว่าบันทึกทั้งสองชนิดเกี่ยวข้องกับวัตถุและ tuples ทั้งสองชนิดเกี่ยวข้องกับอาร์เรย์ แต่นั่นคือความคล้ายคลึงกันสิ้นสุดลง
บันทึกใน typeScript เป็นประเภทยูทิลิตี้ทั่วไปเพื่อแสดงวัตถุที่ใช้การจับคู่ประเภทคีย์กับประเภทค่า พวกเขายังคงเป็นตัวแทนของวัตถุ
ในทำนองเดียวกัน tuples ใน typeScript เป็นสัญลักษณ์ของการแสดงประเภทในอาร์เรย์ที่มีขนาด จำกัด (เริ่มต้นด้วย typeScript 4.0 พวกเขามีรูปแบบที่หลากหลาย) tuples ใน typeScript เป็นวิธีการแสดงอาร์เรย์ที่มีประเภทต่างกัน tuples ECMASCRIPT สามารถสอดคล้องกับอาร์เรย์ TS หรือ TS tuples ได้อย่างง่ายดายเนื่องจากสามารถมีจำนวนค่าไม่ จำกัด ของประเภทเดียวกันหรือมีจำนวน จำกัด ที่มีประเภทที่แตกต่างกัน
TS Records หรือ Tuples เป็นคุณสมบัติมุมฉากสำหรับ Ecmascript Records และ Tuples และทั้งคู่สามารถแสดงออกได้ในเวลาเดียวกัน:
const record : Readonly < Record < string , number > > = # {
foo : 1 ,
bar : 2 ,
} ;
const tuple : readonly [ number , string ] = # [ 1 , "foo" ] ;
ข้อเสนอนี้ไม่ได้ทำการรับประกันประสิทธิภาพใด ๆ และไม่จำเป็นต้องมีการปรับให้เหมาะสมเฉพาะในการใช้งาน จากข้อเสนอแนะจากผู้ดำเนินการคาดว่าพวกเขาจะดำเนินการทั่วไปผ่านอัลกอริทึม "เวลาเชิงเส้น" อย่างไรก็ตามข้อเสนอนี้ไม่ได้ป้องกันการเพิ่มประสิทธิภาพแบบคลาสสิกสำหรับโครงสร้างข้อมูลที่ใช้งานได้อย่างหมดจดรวมถึง แต่ไม่ จำกัด เพียง:
การปรับให้เหมาะสมเหล่านี้มีความคล้ายคลึงกับวิธีที่เครื่องยนต์จาวาสคริปต์สมัยใหม่จัดการกับการเชื่อมต่อสตริงด้วยสตริงภายในประเภทต่าง ๆ ความถูกต้องของการเพิ่มประสิทธิภาพเหล่านี้ขึ้นอยู่กับความไม่สามารถสังเกตได้ของตัวตนของบันทึกและ tuples ไม่คาดว่าเครื่องยนต์ทั้งหมดจะทำหน้าที่เหมือนกันกับการเพิ่มประสิทธิภาพเหล่านี้ แต่พวกเขาแต่ละคนจะตัดสินใจเกี่ยวกับการใช้ฮิวริสติกใดที่จะใช้ ก่อนขั้นตอนที่ 4 ของข้อเสนอนี้เราวางแผนที่จะเผยแพร่คำแนะนำสำหรับแนวทางปฏิบัติที่ดีที่สุดสำหรับการใช้บันทึกและ tuples ที่เหมาะสมที่สุดในการใช้งาน
โครงสร้างข้อมูลแบบดั้งเดิมแบบดั้งเดิมที่ไม่เปลี่ยนรูปแบบใหม่ที่เสนอในเอกสารนี้ซึ่งคล้ายคลึงกับวัตถุ #{ a: 1, b: 2 }
โครงสร้างข้อมูลแบบดั้งเดิมแบบดั้งเดิมที่ไม่เปลี่ยนแปลงอย่างลึกล้ำที่เสนอในเอกสารนี้ซึ่งคล้ายคลึงกับอาร์เรย์ #[1, 2, 3, 4]
ค่าที่ทำหน้าที่เหมือน JavaScript Primitives อื่น ๆ แต่ประกอบด้วยค่าส่วนประกอบอื่น ๆ เอกสารนี้เสนอสองประเภทแรกของสารประกอบดั้งเดิม: Record
และ Tuple
String
, Number
, Boolean
, undefined
, null
, Symbol
และ BigInt
สิ่งที่เป็นสารประกอบหรือแบบดั้งเดิมที่เรียบง่าย ดั้งเดิมทั้งหมดใน JavaScript แบ่งปันคุณสมบัติบางอย่าง:
โครงสร้างข้อมูลที่ไม่ยอมรับการดำเนินการที่เปลี่ยนแปลงภายใน แต่แทนที่จะมีการดำเนินการที่ส่งคืนค่าใหม่ซึ่งเป็นผลมาจากการใช้การดำเนินการนั้น
ใน Record
ข้อเสนอนี้และ Tuple
เป็นโครงสร้างข้อมูลที่ไม่เปลี่ยนแปลงอย่างลึกซึ้ง
ตัวดำเนินการ ===
ถูกกำหนดด้วยอัลกอริทึมการเปรียบเทียบความเท่าเทียมกันอย่างเข้มงวด ความเท่าเทียมกันอย่างเข้มงวดหมายถึงแนวคิดเรื่องความเสมอภาคนี้โดยเฉพาะ
การแบ่งปันโครงสร้างเป็นเทคนิคที่ใช้ในการ จำกัด รอยเท้าหน่วยความจำของโครงสร้างข้อมูลที่ไม่เปลี่ยนรูป โดยสรุปเมื่อใช้การดำเนินการเพื่อหาโครงสร้างรุ่นใหม่ที่ไม่เปลี่ยนรูปแบบการแบ่งปันโครงสร้างจะพยายามรักษาโครงสร้างภายในส่วนใหญ่ไว้เหมือนเดิมและใช้งานโดยทั้งรุ่นเก่าและที่ได้รับของโครงสร้างนั้น นี่เป็นการ จำกัด จำนวนเงินที่จะคัดลอกเพื่อหาโครงสร้างใหม่อย่างมาก