해당 포스트는
Array.prototype
의 특정 메서드들을 직접 구현한 포스트입니다.
⭕ -> 원본 자체를 변경하는 메서드
❌ -> 원본을 변경하지 않는 메서드
✈️ 일반 배열 메서드들
0️⃣ ( ❌ ) Array.prototype.slice()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* ❌ "Array.prototype.slice()" 구현
* @param { number } begin 시작 인덱스
* @param { number } end 끝 인덱스
* @returns { any[] } 복사된 배열
*
* + 조건
* 1.1 "begin"이 배열의 길이보다 길다면 빈 배열 반환
* 1.2 "begin"이 음수라면 뒤에서부터 시작
* 1.3 "begin"이 "undefined"라면 "0"부터 시작
*
* 2.1 "end"번째 인덱스를 제외
* 2.2 "end"가 음수라면 뒤에서부터 계산
* 2.3 "end"가 생략되면 끝까지
*/
Array.prototype.mySlice = function (begin = 0, end = this.length) {
const myArray = [];
// 1.2 "begin"이 음수라면
if (begin < 0) {
begin = this.length + begin;
}
// 2.2 "end"가 음수라면
if (end < 0) {
end = this.length + end;
}
for (let index = begin; index < end; index++) {
myArray.push(this[index]);
}
return myArray;
};
1️⃣ ( ⭕ ) Array.prototype.splice()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* ⭕ "Array.prototype.splice()" 구현
* @param { number } start 배열의 변경을 시작할 인덱스
* @param { number } deleteCount 배열에서 제거할 요소 수
* @param { ...any } items 배열에 추가할 요소들
* @return { any[] } 제거한 요소들을 담은 배열 반환 ( 제거하지 않은 경우 빈 배열 )
*
* + 조건
* 1.1 "start"가 배열의 길이보다 크다면 배열의 길이
* 1.2 "start"가 음수면 뒤에서부터 시작
* 1.3 "start"의 음수이면서 절댓값이 배열의 길이보다 큰 경우 0
*
* 2.1 "deleteCount" 생략하거나 "array.length - start"보다 크면 "start"부터 모든 요소 제거
* 2.2 "deleteCount" 0이하라면 제거하지 않으며 최소 하나의 "item"이 필요함
*/
Array.prototype.mySplice = function (start, deleteCount, ...items) {
const deletedItems = [];
let copyItems = [];
// 1.2 음수라면
if (start < 0) {
start = this.length + start;
}
// 1.3 음수면서 절댓값이 배열의 길이보다 크면
if (start < 0) {
start = 0;
}
// 잘려진 뒷부분 아이템들 ( 나중에 결합 )
copyItems = this.mySlice(start);
// 뒷부분 잘라버리기
this.length = start;
// 원본 배열에 추가하기
for (let index = 0; index < items.length; index++) {
this.push(items[index]);
}
// 버러진 아이템 등록
for (let index = 0; index < deleteCount; index++) {
deletedItems.push(copyItems.shift());
}
// 원본 배열에 남은 아이템 넣기
for (let index = 0; index < copyItems.length; index++) {
this.push(copyItems[index]);
}
return deletedItems;
};
2️⃣ ( ❌ ) Array.prototype.indexOf()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* ❌ "Array.prototype.indexOf()" 구현
* @param { any } target 인덱스를 찾고 싶은 아이템
* @returns { number } 찾은 인덱스 ( 없으면 -1 )
*
* + 조건
* 1. 특정 대상을 찾을 수 없으면 "-1"반환
*/
Array.prototype.myIndexOf = function (target) {
let targetIndex = -1;
for (let index = 0; index < this.length; index++) {
if (target === this[index]) {
targetIndex = index;
break;
}
}
return targetIndex;
};
3️⃣ ( ❌ ) Array.prototype.includes()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* ❌ "Array.prototype.includes()" 구현
* @param { any } target 존재하는지 찾을 아이템
* @returns { boolean } 존재 여부
*/
Array.prototype.myIncludes = function (target) {
let isExist = false;
for (let index = 0; index < this.length; index++) {
if (target === this[index]) {
isExist = true;
break;
}
}
return isExist;
};
4️⃣ ( ❌ ) Array.prototype.join()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* ❌ "Array.prototype.join()" 구현
* @param { string } separator 구분자
*/
Array.prototype.myJoin = function (separator = ",") {
let joinedString = "";
for (let index = 0; index < this.length; index++) {
if (index === this.length - 1) {
joinedString += this[index];
} else {
joinedString += this[index] + separator;
}
}
return joinedString;
};
5️⃣ ( ⭕ ) Array.prototype.reverse()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* ⭕ "Array.prototype.reverse()" 구현
* @returns { any[] } 반전된 배열
*/
Array.prototype.myReverse = function () {
for (let index = 0; index < Math.floor(this.length / 2); index++) {
[this[index], this[this.length - 1 - index]] = [
this[this.length - 1 - index],
this[index],
];
}
return this;
};
6️⃣ ( ❌ ) Array.prototype.flat()
FIXME: depth
에 따라서 평탄화되도록
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* ❌ "Array.prototype.flat()" 구현
* @param { number } depth 평탄화 할 깊이
* @returns { any[] } 평탄화된 배열
*/
Array.prototype.myFlat = function (depth = 1) {
let copiedArray = this.mySlice();
let tempArray = null;
// depth 사용해야 함
for (let index = 0; index < copiedArray.length; index++) {
if (Array.isArray(copiedArray[index])) {
[tempArray] = copiedArray.mySplice(index, 1);
copiedArray = [...copiedArray, ...tempArray];
}
}
return copiedArray;
};
🚀 고차 함수를 사용하는 배열 메서드들
FIXME: <empty>
는 순회에서 제외하기 ( for ~ in
으로 가능하긴 하지만 반복문에 사용하긴 비효율적이라고 생각함, in
생각해보기 )
0️⃣ ( ❌ ) Array.prototype.forEach()
1
2
3
4
5
6
7
8
9
10
/**
* ❌ "Array.prototype.forEach()" 구현
* @param { (value: any, index: number, self: any[]) => void } callback 각 인자에 맞게 호출할 콜백함수
* @param { object } thisArgs 바인딩할 "this" 값
*/
Array.prototype.myForEach = function (callback, thisArgs) {
for (let index = 0; index < this.length; index++) {
callback.call(thisArgs || globalThis, this[index], index, this);
}
};
1️⃣ ( ❌ ) Array.prototype.filter()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* ❌ "Array.prototype.filter()" 구현
* @param { (value: any, index: number, self: any[]) => void } callback 각 인자에 맞게 호출할 콜백함수
* @param { object } thisArgs 바인딩할 "this" 값
* @returns { any[] } "callback"의 리턴 값이 참인 아이템을 모아서 새로운 배열로 만들어 반환
*/
Array.prototype.myFilter = function (callback, thisArgs) {
const filteredArray = [];
for (let index = 0; index < this.length; index++) {
if (!callback.call(thisArgs || globalThis, this[index], index, this)) {
continue;
}
filteredArray.push(this[index]);
}
return filteredArray;
};
2️⃣ ( ❌ ) Array.prototype.map()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* ❌ "Array.prototype.map()" 구현
* @param { (value: any, index: number, self: any[]) => void } callback 각 인자에 맞게 호출할 콜백함수
* @param { object } thisArgs 바인딩할 "this" 값
* @returns { any[] } "callback"의 리턴들을 모아서 새로운 배열로 만들어 반환
*/
Array.prototype.myMap = function (callback, thisArgs) {
const mappedArray = [];
for (let index = 0; index < this.length; index++) {
mappedArray.push(
callback.call(thisArgs || globalThis, this[index], index, this)
);
}
return mappedArray;
};
3️⃣ ( ❌ ) Array.prototype.every()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* ❌ "Array.prototype.every()" 구현
* @param { (value: any, index: number, self: any[]) => void } callback 각 인자에 맞게 호출할 콜백함수
* @param { object } thisArgs 바인딩할 "this" 값
* @returns { boolean } 모두 참이면 true 아니면 false
*/
Array.prototype.myEvery = function (callback, thisArgs) {
for (let index = 0; index < this.length; index++) {
if (!callback.call(thisArgs || globalThis, this[index], index, this))
return false;
}
return true;
};
4️⃣ ( ❌ ) Array.prototype.some()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* ❌ "Array.prototype.some()" 구현
* @param { (value: any, index: number, self: any[]) => void } callback 각 인자에 맞게 호출할 콜백함수
* @param { object } thisArgs 바인딩할 "this" 값
* @returns { boolean } 하나라도 true면 true 아니면 false
*/
Array.prototype.mySome = function (callback, thisArgs) {
for (let index = 0; index < this.length; index++) {
if (callback.call(thisArgs || globalThis, this[index], index, this)) {
return true;
}
}
return false;
};
5️⃣ ( ❌ ) Array.prototype.find()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* ❌ "Array.prototype.find()" 구현
* @param { (value: any, index: number, self: any[]) => void } callback 각 인자에 맞게 호출할 콜백함수
* @param { object } thisArgs 바인딩할 "this" 값
* @returns { any } 조건에 만족하는 아이템 리턴
*/
Array.prototype.myFind = function (callback, thisArgs) {
let result = null;
for (let index = 0; index < this.length; index++) {
result = callback.call(thisArgs || globalThis, this[index], index, this);
if (result) {
return this[index];
}
}
};
6️⃣ ( ❌ ) Array.prototype.findIndex()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* ❌ "Array.prototype.findIndex()" 구현
* @param { (value: any, index: number, self: any[]) => void } callback 각 인자에 맞게 호출할 콜백함수
* @param { object } thisArgs 바인딩할 "this" 값
* @returns { any } 조건에 만족하는 아이템의 인덱스 리턴
*/
Array.prototype.myFindIndex = function (callback, thisArgs) {
let result = null;
for (let index = 0; index < this.length; index++) {
result = callback.call(thisArgs || globalThis, this[index], index, this);
if (result) {
return index;
}
}
};
7️⃣ ( ❌ ) Array.prototype.reduce()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* ❌ "Array.prototype.findIndex()" 구현
* @param { (accumulator: any, currentValue: any, currentIndex: number self: any[]) => void } callback 각 인자에 맞게 호출할 콜백함수
* @param { any } initialValue 초깃값
* @returns { any } 모든 배열을 순회하고 누적된 값 반환
*/
Array.prototype.myReduce = function (callback, initialValue) {
let accumulatorValue = initialValue;
for (let index = 0; index < this.length; index++) {
if (accumulatorValue && index === 0) {
continue;
}
accumulatorValue = callback(accumulatorValue, this[index], index, this);
}
return accumulatorValue;
};
8️⃣ ( ⭕ ) Array.prototype.sort()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* ⭕ "Array.prototype.sort()" 구현
* ( Merge Sort 사용 )
*
* @param { (a, b) => number } compareFunction 0보다 작으면 내림차순, 0이면 그대로, 0보다 크면 오름차순
*/
Array.prototype.mySort = function (compareFunction) {
const merge = (left, right, compareFunction) => {
if (!left || !right) return [...left, ...right];
const tempArray = [];
// 비교 함수가 있는 경우
if (typeof compareFunction === "function") {
while (left.length !== 0 && right.length !== 0) {
const isAscending = compareFunction(left[0] + "", right[0] + "");
// 0보다 큰 경우 ( 내림차순 )
if (isAscending > 0) {
tempArray.push(right.shift());
}
// 0보다 작은 경우 ( 오름차순 )
else if (isAscending < 0) {
tempArray.push(left.shift());
}
// 0인경우 아무것도 하지 않음
else {
return [...tempArray, ...left, ...right];
}
}
}
// 비교 함수가 없는 경우 ( 오름차순 )
else {
while (left.length !== 0 && right.length !== 0) {
// 좌측이 더 크다면 우측 값을 먼저 추가
if (left[0] + "" > right[0] + "") {
tempArray.push(right.shift());
}
// 우측이 더 크다면 좌측 값을 먼저 추가
else {
tempArray.push(left.shift());
}
}
}
return [...tempArray, ...left, ...right];
};
const mergeSort = (array, compareFunction) => {
// 배열의 요소가 하나 남을 때까지 반으로 나눈다.
if (array.length === 1) return array;
const mid = array.length / 2;
const left = array.slice(0, mid);
const right = array.slice(mid);
return merge(
mergeSort(left, compareFunction),
mergeSort(right, compareFunction),
compareFunction
);
};
let sortedArray = mergeSort(this, compareFunction);
// 원본 변경
this.length = 0;
this.push(...sortedArray);
};
📮 레퍼런스
- MDN - Array.prototype.slice()
- MDN - Array.prototype.splice()
- MDN - Array.prototype.indexOf()
- MDN - Array.prototype.includes()
- MDN - Array.prototype.reverse()
- MDN - Array.prototype.flat()
- MDN - Array.prototype.forEach()
- MDN - Array.prototype.filter()
- MDN - Array.prototype.map()
- MDN - Array.prototype.every()
- MDN - Array.prototype.some()
- MDN - Array.prototype.find()
- MDN - Array.prototype.findIndex()
- MDN - Array.prototype.reduce()
- MDN - Array.prototype.sort()
- 1-blue - Merge Sort