Как деструктуризация массивов может замедлить JavaScript код
ArrayAssignmentPattern
не всегда является лучшим выбором.Сколько шаблонов деструктуризации существует в JavaScript
В JavaScript существует два шаблона для Деструктурирующего присваивания
ArrayAssignmentPattern
Давайте рассмотрим алгоритм ArrayAssignmentPattern
в спецификации Ecma262 для примера const [ first, second ] = [ 1, 2 ]
ArrayAssignmentPattern : [ AssignmentElementList ]
1. Let iteratorRecord be ? GetIterator(value, sync).
2. Let result be Completion(IteratorDestructuringAssignmentEvaluation of AssignmentElementList with argument iteratorRecord).
3. If iteratorRecord.[[Done]] is false, return ? IteratorClose(iteratorRecord, result).
4. Return result.
Как видим, в алгоритме создаётся итератор, требующий много ресурсов.
function arrayAssignmentPattern(){
const [ first, second ] = [ 1, 2 ]
return first, second;
}
arrayAssignmentPattern()
Теперь посмотрим на байткод (V8) этого кода
CreateArrayLiteral [0], [0], #37
Star2
GetIterator r2, [1], [3]
Star4
GetNamedProperty r4, [1], [5]
Star3
LdaFalse
Star5
LdaTheHole
Star8
Mov <context>, r9
Ldar r5
JumpIfToBooleanTrue [33] (0x2b1a00040115 @ 57)
LdaTrue
Star5
CallProperty0 r3, r4, [11]
Star10
JumpIfJSReceiver [7] (0x2b1a00040104 @ 40)
CallRuntime [ThrowIteratorResultNotAnObject], r10-r10
GetNamedProperty r10, [2], [9]
JumpIfToBooleanTrue [13] (0x2b1a00040115 @ 57)
GetNamedProperty r10, [3], [7]
Star10
LdaFalse
Star5
Ldar r10
Jump [3] (0x2b1a00040116 @ 58)
LdaUndefined
Star0
Ldar r5
JumpIfToBooleanTrue [33] (0x2b1a0004013a @ 94)
LdaTrue
Star5
CallProperty0 r3, r4, [13]
Star10
JumpIfJSReceiver [7] (0x2b1a00040129 @ 77)
CallRuntime [ThrowIteratorResultNotAnObject], r10-r10
GetNamedProperty r10, [2], [9]
JumpIfToBooleanTrue [13] (0x2b1a0004013a @ 94)
GetNamedProperty r10, [3], [7]
Star10
LdaFalse
Star5
Ldar r10
Jump [3] (0x2b1a0004013b @ 95)
LdaUndefined
Star1
LdaSmi [-1]
Star7
Star6
Jump [8] (0x2b1a00040148 @ 108)
Star7
LdaZero
Star6
LdaTheHole
SetPendingMessage
Star8
Ldar r5
JumpIfToBooleanTrue [35] (0x2b1a0004016d @ 145)
Mov <context>, r11
GetNamedProperty r4, [4], [15]
JumpIfUndefinedOrNull [26] (0x2b1a0004016d @ 145)
Star12
CallProperty0 r12, r4, [17]
JumpIfJSReceiver [19] (0x2b1a0004016d @ 145)
Star13
CallRuntime [ThrowIteratorResultNotAnObject], r13-r13
Jump [11] (0x2b1a0004016d @ 145)
Star11
LdaZero
TestReferenceEqual r6
JumpIfTrue [5] (0x2b1a0004016d @ 145)
Ldar r11
ReThrow
LdaZero
TestReferenceEqual r6
JumpIfFalse [8] (0x2b1a00040178 @ 156)
Ldar r8
SetPendingMessage
Ldar r7
ReThrow
Ldar r1
Return
ObjectAssignmentPattern
Рассмотрим алгоритм ObjectAssignmentPattern
в спецификации Ecma262 для примера const {0 : first, 1: second } = [ 1, 2 ]
.
ObjectAssignmentPattern :
{ AssignmentPropertyList }
{ AssignmentPropertyList , }
1. Perform ? RequireObjectCoercible(value).
2. Perform ? PropertyDestructuringAssignmentEvaluation of AssignmentPropertyList with argument value.
3. Return unused.
Как видно, этот алгоритм не создаёт итератор, и можно предположить, что он будет работать быстрее, чем ArrayAssignmentPattern
, но не будем на этом останавливаться, а продолжим наше исследование🧐.
function objectAssignmentPattern(){
const { 0: first, 1: second} = [ 1, 2 ]
return first, second;
}
objectAssignmentPattern()
Теперь посмотрим на байткод (V8) этого кода
CreateArrayLiteral [0], [0], #37
Star2
LdaZero
Star3
GetKeyedProperty r2, [1]
Star0
LdaSmi [1]
Star3
GetKeyedProperty r2, [3]
Star1
Ldar r1
Return
В случае ObjectAssignmentPattern
, GetKeyedProperty
используется для получения значения из объекта по определённому ключу или индексу. И как видим, между ArrayAssignmentPattern
и ObjectAssignmentPattern
существует большая разница в объёме кода, даже если ничего не понимать в байткоде.
Время для тестирования
Как и предполагалось, ObjectAssignmentPattern
будет работать быстрее и с меньшими затратами ресурсов, чем ArrayAssignmentPattern
. Давайте проверим это и посмотрим, что покажут цифры.
console.time('ArrayAssignmentPattern');
for (let i = 0; i < 100000; i++) {
let [a, b] = [1,2];
}
console.timeEnd('ArrayAssignmentPattern');
console.time('ObjectAssignmentPattern');
for (let i = 0; i < 100000; i++) {
let { 0: a, 1: b } = [1,2];
}
console.timeEnd('ObjectAssignmentPattern');
Посмотрим, что получится в консоли 👇.
Как видим, в этом тесте ObjectAssignmentPattern
примерно (как минимум) в 3 раза быстрее, чем ArrayAssignmentPattern
, демонстрируя прирост производительности на 200%.