Skip to content

Commit

Permalink
perf: Improve FormData
Browse files Browse the repository at this point in the history
  • Loading branch information
tsctx committed Dec 29, 2023
1 parent d432961 commit 9a78292
Showing 1 changed file with 69 additions and 25 deletions.
94 changes: 69 additions & 25 deletions lib/fetch/formdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ class FormData {

// The delete(name) method steps are to remove all entries whose name
// is name from this’s entry list.
this[kState] = this[kState].filter(entry => entry.name !== name)
const state = this[kState]
for (let i = 0; i < state.length; ++i) {
if (state[i].name === name) {
state.splice(i, 1)
--i
}
}
}

get (name) {
Expand All @@ -73,14 +79,15 @@ class FormData {

// 1. If there is no entry whose name is name in this’s entry list,
// then return null.
const idx = this[kState].findIndex((entry) => entry.name === name)
if (idx === -1) {
return null
}

// 2. Return the value of the first entry whose name is name from
// this’s entry list.
return this[kState][idx].value
const state = this[kState]
for (let i = 0; i < state.length; ++i) {
if (state[i].name === name) {
return state[i].value
}
}
return null
}

getAll (name) {
Expand All @@ -94,9 +101,14 @@ class FormData {
// then return the empty list.
// 2. Return the values of all entries whose name is name, in order,
// from this’s entry list.
return this[kState]
.filter((entry) => entry.name === name)
.map((entry) => entry.value)
const state = this[kState]
const result = []
for (let i = 0; i < state.length; ++i) {
if (state[i].name === name) {
result.push(state[i].value)
}
}
return result
}

has (name) {
Expand All @@ -108,7 +120,13 @@ class FormData {

// The has(name) method steps are to return true if there is an entry
// whose name is name in this’s entry list; otherwise false.
return this[kState].findIndex((entry) => entry.name === name) !== -1
const state = this[kState]
for (let i = 0; i < state.length; ++i) {
if (state[i].name === name) {
return true
}
}
return false
}

set (name, value, filename = undefined) {
Expand Down Expand Up @@ -141,24 +159,31 @@ class FormData {

// 3. If there are entries in this’s entry list whose name is name, then
// replace the first such entry with entry and remove the others.
const idx = this[kState].findIndex((entry) => entry.name === name)
if (idx !== -1) {
this[kState] = [
...this[kState].slice(0, idx),
entry,
...this[kState].slice(idx + 1).filter((entry) => entry.name !== name)
]
} else {
const state = this[kState]
let found = false
for (let i = 0; i < state.length; ++i) {
if (state[i].name === name) {
if (!found) {
state[i] = entry
found = true
} else {
state.splice(i, 1)
--i
}
}
}
if (!found) {
// 4. Otherwise, append entry to this’s entry list.
this[kState].push(entry)
state.push(entry)
}
}

entries () {
webidl.brandCheck(this, FormData)

const state = this[kState]
return makeIterator(
() => this[kState].map(pair => [pair.name, pair.value]),
() => getEntiresFromState(state),
'FormData',
'key+value'
)
Expand All @@ -167,8 +192,9 @@ class FormData {
keys () {
webidl.brandCheck(this, FormData)

const state = this[kState]
return makeIterator(
() => this[kState].map(pair => [pair.name, pair.value]),
() => getEntiresFromState(state),
'FormData',
'key'
)
Expand All @@ -177,8 +203,9 @@ class FormData {
values () {
webidl.brandCheck(this, FormData)

const state = this[kState]
return makeIterator(
() => this[kState].map(pair => [pair.name, pair.value]),
() => getEntiresFromState(state),
'FormData',
'value'
)
Expand All @@ -199,8 +226,11 @@ class FormData {
)
}

for (const [key, value] of this) {
callbackFn.apply(thisArg, [value, key, this])
// If the state is changed, you can still safely use `for(;;)`
// because the value will change.
const state = this[kState]
for (let i = 0; i < state.length; ++i) {
callbackFn.call(thisArg, state[i].value, state[i].name, this)
}
}
}
Expand Down Expand Up @@ -262,4 +292,18 @@ function makeEntry (name, value, filename) {
return { name, value }
}

/**
* @param {unknown} formDataState
* @returns {[string, string | File][]}
*/
function getEntiresFromState (formDataState) {
const stateLength = formDataState.length
// Note: avoid `Array#push`
const result = new Array(stateLength)
for (let i = 0; i < stateLength; ++i) {
result[i] = [formDataState[i].name, formDataState[i].value]
}
return result
}

module.exports = { FormData }

0 comments on commit 9a78292

Please sign in to comment.