티스토리 뷰
Array.from: 메서드 분석하기
Array.from()이라는 메서드를 알고 있었다. 나는 이 메서드가 '문자열을 배열로 변환'하는 메서드라고 인지하고 정리해두었는데, 강의에서 나오는 코드를 보다 보니, 잘못된 정의였다는 걸 알게 되었다.
Array.from()은 “유사 배열(array-like) 객체나 iterable을 진짜 배열로 바꿔주는" 메서드로 정리할 수 있고, 좀 더 내부 동작을 살펴보면 더 흥미로운 점이 나타난다.
이번 글에서는 직접 공부하면서 헷갈렸던 부분(특히 this가 생성자가 된다는 설명)을 정리하고, ECMAScript 사양을 바탕으로 Array.from의 내부 동작을 해설해보겠다.
1. 기본 사용법
Array.from(arrayLike)Array.from(arrayLike, mapFn)Array.from(arrayLike, mapFn, thisArg)
매개변수
arrayLike
배열로 변환할 순회 가능iterable 또는 유사 배열 객체
mapFnOptional
배열의 모든 요소에 대해 호출할 함수. 이 함수를 제공하면 배열에 추가할 모든 값이 이 함수를 통해 먼저 전달되고, mapFn의 반환 값이 대신 배열에 추가된다. 이 함수는 다음 인수를 사용하여 호출된다.
element
배열에서 처리 중인 현재 요소.index
배열에서 처리 중인 현재 요소의 인덱스.
thisArgOptional
mapFn 실행 시에 this로 사용할 값.
반환 값
새로운 Array 인스턴스
// 배열로 변환
console.log(Array.from("hello")); // ["h", "e", "l", "l", "o"]
// Set → 배열
console.log(Array.from(new Set([1, 2, 3]))); // [1, 2, 3]
// mapFn 사용
console.log(Array.from([1, 2, 3], x => x * 2)); // [2, 4, 6]
이 정도는 흔히 아는 활용법이라고 한다. 그런데 문서를 보다 보면 이런 문장이 나온다.:
Array의 하위 클래스가 from() 메서드를 상속하는 경우, 상속된 from() 메서드는 Array 인스턴스 대신 하위 클래스의 새 인스턴스를 반환합니다. 실제로 this 값은 새 배열의 길이를 나타내는 단일 인수를 받는 모든 생성자 함수가 될 수 있습니다.
처음 읽으면 “this가 생성자가 된다니 뭔 소리지” 싶었다.
2. this가 생성자가 된다는 뜻
사실 Array.from은 내부적으로 대략 이렇게 동작한다:
Array.from = function(arrayLike) {
const C = this; // this를 "생성자"로 간주
const len = arrayLike.length >>> 0;
const newObj = new C(len); // new this(length)
// 요소 채우기...
return newObj;
};
즉, Array.from은 단순히 배열을 만드는 게 아니라 “this로 받은 생성자를 이용해서 새 인스턴스를 만든다”는 게 핵심이다.
3. 하위 클래스 예시
class MyArray extends Array {}
const myArr = MyArray.from([10, 20, 30]);
console.log(myArr instanceof MyArray); // true
console.log(myArr); // MyArray(3) [10, 20, 30]
보통은 Array 인스턴스를 반환하지만, 하위 클래스에서 호출하면 해당 클래스의 인스턴스를 반환한다.
이게 바로 this가 생성자가 된다는 의미이다.
4. 커스텀 생성자와 Array.from
심지어 Array가 아닌, 완전히 다른 생성자를 this로 바인딩할 수도 있다.
function FakeArray(length) {
this.length = length;
}
const fake = Array.from.call(FakeArray, { length: 2, 0: "a", 1: "b" });
console.log(fake instanceof FakeArray); // true
console.log(fake); // FakeArray {0: "a", 1: "b", length: 2}
이 경우 내부에서 new FakeArray(length)가 실행되기 때문에 FakeArray 인스턴스를 얻게 된다.
5. iterable vs array-like를 배열로 만들 때 동작 차이
- iterable (
Set,Map,string등)
→ 일단 빈 객체(new this(0))를 만든 후, 순회하면서 하나씩 채워 넣음. - array-like (
{ length: 3, 0: "a", 1: "b" })
→new this(length)로 정확한 길이의 인스턴스를 만들고, 인덱스를 채움. - 마지막에
length값을 다시 조정.
6. 공식 사양에서의 정의
ECMAScript® 2026 사양 23.1.2.1 Array.from 절에 명확히 정의되어 있다. 실제 코드가 아닌, 알고리즘 단계(pseudocode)로 기술되어 있는데, 핵심 단계는 다음과 같다:
this를 생성자로 취급 (ArraySpeciesCreate호출).items가 iterable이면 iterator를 얻어 순회.- array-like면 length를 구해 인스턴스 생성.
- mapFn이 있으면 요소마다 변환 적용.
- 최종적으로 새 인스턴스 반환.
즉, 사양은 “어떤 JS 코드”를 보여주는 게 아니라 모든 엔진이 따라야 하는 규칙을 정의한다. V8, SpiderMonkey, JavaScriptCore 같은 엔진들은 이걸 C++ 등으로 구현해 놓았다.
7. 직접 모방 구현 (JS 버전)
공식 엔진 코드는 C++로 되어 있으니, 여기서는 JS로 흉내만 내본 버전:
function from(arrayLike, mapFn, thisArg) {
const C = this || Array;
const obj = Object(arrayLike);
const len = obj.length >>> 0;
const A = new C(len); // this를 생성자로 사용
for (let k = 0; k < len; k++) {
let kValue = obj[k];
if (mapFn) {
kValue = mapFn.call(thisArg, kValue, k);
}
A[k] = kValue;
}
A.length = len;
return A;
}
// 테스트
console.log(from.call(Array, { length: 3, 0: "x", 1: "y" }));
결론
Array.from은 단순 변환 도우미가 아니라, 생성자 바인딩이 가능한 범용 팩토리 메서드this가Array일 수도,Array하위 클래스일 수도, 심지어 아예 다른 생성자일 수도 있다.- 사양에서는 실행 코드가 아닌 의사코드 알고리즘으로 정의되어 있고, 실제 구현은 엔진마다 다르다.
✍️ 글을 쓰면서 느낀 점은, 공부한 것 정리할 땐 편하려고 불명확한 말을 쓰지 말아야겠다는 점이었다. 그리고 또, 귀찮더라도 표준 문서의 내용을 이해하는 게 중요할 것 같다.
'Programming > JavaScript' 카테고리의 다른 글
| [JavaScript] 자료구조 구성을 위한 ES2022 클래스 문법 정리 (0) | 2025.10.13 |
|---|---|
| [JavaScript] 브라우저 이벤트 : 이벤트 위임(Event Delegation) (1) | 2025.01.12 |
| [JavaScript] 브라우저 이벤트 : 버블링(Bubbling)과 캡처링(Capturing) (2) | 2025.01.06 |
| Vanila 프로젝트 후기 (1) | 2024.12.31 |
| [JavaScript] Throttle과 Debounce (1) | 2024.12.15 |
