Skip to main content

Command Palette

Search for a command to run...

[6장] 프로토타입

Published
•11 min read•View as Markdown
[6장] 프로토타입
W

안녕하세요! 프론트엔드 개발자 woodstock입니다.

저는 매일 조금씩 발전하고자 하는 마음으로 개발공부를 시작했고, 이 블로그는 그 과정에서 배우고 성장하는 이야기를 담고 있습니다.

여러분의 피드백과 조언은 언제나 환영합니다! 함께 배우고 성장하는 과정을 즐길 수 있기를 기대합니다.

1. 프로토타입의 개념 이해

1-1 constructor, prototype, instance

이미지 출처: "코어 자바스크립트", 정재남, 위키북스, 2019, p. 147.

이 그림은 다음의 코드를 추상화한 것이다.

var instance = new Constructor();

좀 더 구체적인 형태로 바꾸면 다음과 같다.

이미지 출처: "코어 자바스크립트", 정재남, 위키북스, 2019, p. 148.

  • 어떤 생성자 함수(Constructor)를 new연산자와 함께 호출하면
  • Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스(instance)가 생성된다.
  • 이때 instance에는 __proto__라는 프로퍼티가 자동으로 부여되는데,
  • 이 프로퍼티는 Constructor의 prototype이라는 프로퍼티를 참조한다.

prototype과 __proto__의 관계가 프로토타입 개념의 핵심이다.

prototype은 객체이다. 이를 참조하는 __proto__역시 객체일 것이다. prototype객체 내부에는 인스턴스가 사용할 메서드를 저장하는데, 이 때문에 인스턴스에서도 숨겨진 프로퍼티인 __proto__를 통해 이 메서드들에 접근할 수 있게 된다.

ES5.1 명세에는 __proto__가 아닌 [[prototype]]이라는 명칭으로 정의되어 있는데, 이는 브라우저들이 [[prototype]]을 __proto__로 구현한 것일 뿐 명세에서는 instance.__proto__와 같은 방식으로 직접 접근하는 것을 허용하지 않기 때문이다. ES6에서는 브라우저에서 동작하는 레거시 코드에 대한 호환성 유지 차원에서 __proto__를 정식으로 인정하기는 했지만 권장되는 방식은 아니며, Object.getPrototypeOf() / Obhect.create()와 같은 메서드를 사용하는 것이 좋다.

다만 본문에서는 이해의 편의를 위해 __proto__를 사용하려 한다.


Person이라는 생성자 함수의 prototype에 getName이라는 메서드를 지정했다고 해보자.

Person.prototype

var Person = function (name) {
  this._name = name;
};
Person.prototype.getName = function () {
  return this._name;
};

이제 Person의 인스턴스는 __proto__프로퍼티를 통해 getName을 호출할 수 있다,

var suzi = new Person("Suzi");
suzi.__proto__.getName(); // undefined

instance의 __proto__가 Constructor의 prototype프로퍼티를 참조하므로 결국 둘은 같은 객체를 바라보기 때문이다.

Person.prototype === suzi.__proto__; // true


메서드 호출 결과로 undefined가 나온 점에 주목해보자. 어떤 변수를 실행해 undefined가 나왔다는 것은 이 변수가 '호출할 수 있는 함수'에 해당한다는 것을 의미한다. 즉, suzi.__proto__.getName이 함수를 반환했다는 것이다. 그런데 왜 undefined가 나왔을까?

문제는 this에 바인딩된 대상이 잘못 지정되어 있기 때문이다.

어떤 함수를 '메서드로서' 호출할 때, 메서드명 바로 앞의 객체가 곧 'this'가 된다. 즉, thomas.__proto__.getName()에서 getName함수 내부에서의 this는 thomas가 아니라 thomas.__proto__라는 객체가 되는 것이다. 이 객체 내부에는 name프로퍼티가 없으므로 undefined가 반환되는 것이다.


그럼 __proto__객체에 name프로퍼티가 있다면 어떨까?

var suzi = new Person("Suzi");
suzi.__proto__.name = "SUZI__proto__";
suzi.__proto__.getName(); // SUZI__proto__

예상대로 SUZI__proto__가 잘 출력된다.


그렇다면 this를 인스턴스로 하려면 어떻게 해야 할까?

__proto__없이 인스턴스에서 메서드를 사용하면 된다.

var suzi = new Person("Suzi", 28);
suzi.getName(); // Suzi

var iu = new Person("Jieun", 28);
iu.getName(); // Jieun

이것이 가능한 이유는 __proto__가 생략 가능한 프로퍼티이기 때문이다.

suzi.__proto__.getName
-> suzi(.__proto__).getName
-> suzi.getName

__proto__를 생략하지 않으면 this는 suzi.__proto__를 가리키지만, 생략하면 this는 suzi를 가리키게 된다. suzi.__proto__에 있는 메서드인 getName을 실행하지만 this는 suzi를 바라보게 되는 것이다.

이미지 출처: "코어 자바스크립트", 정재남, 위키북스, 2019, p. 152.

한마디로, "new 연산자로 Constructor를 호출하면 instance가 만들어 지는데, 이 instance의 생략 가능한 프로퍼티인 __proto__는 Constructor의 prototype을 참조한다."라고 정리할 수 있다.


좀 더 상세히 설명하자면 다음과 같다.

자바스크립트는 함수에 자동으로 객체인 prototype프로퍼티를 생성해 놓는데, 해당 함수를 생성자 함수로서 사용할 경우 (new 연산자와 함께 함수를 호출할 경우) 그로부터 생성된 인스턴스에는 숨겨진 프로퍼티인 __proto__가 자동으로 생성되며, 이 프로퍼티는 생성자 함수의 prototype프로퍼티를 참조한다. __proto__프로퍼티는 생략 가능하도록 구현되어 있기 때문에 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있게 된다.

prototype과 __proto__의 관계

var Constructor = function (name) {
  this.name = name;
};
Constructor.prototype.method1 = function () {};
Constructor.prototype.property1 = "Constructor Prototype Property";

var instance = new Constructor("Instance");
console.dir(Constructor);
console.dir(instance);

instance와 Constructor의 디렉터리 구조를 출력해보면 동일한 내용으로 구성되어 있는 것을 확인할 수 있다.


Array를 바탕으로 다시 한 번 살펴보자

var arr = [1, 2];
console.dir(arr);
console.dir(Array);

역시 동일한 내용으로 구성되어 있는 것을 확인할 수 있다.

이 출력 결과를 구체화하면 다음과 같다.

이미지 출처: "코어 자바스크립트", 정재남, 위키북스, 2019, p. 156.

Array를 new 연산자와 함께 호출해서 인스턴스를 생성하든, 그냥 배열 리터럴을 생성하든 instance인 [1, 2]가 만들어진다. 이 인스턴스의 __proto__는 Array.prototype을 참조하는데, __proto__가 생략 가능하도록 설계되어 있기 때문에 인스턴스가 push, pop, forEach등의 메서드를 마치 자신의 것처럼 호출할 수 있다.

반면, Array의 prototype프로퍼티 내부에 있지 않은 from, isArray등의 메서드들은 인스턴스가 직접 호출할 수 없을 것이다.

이들은 Array생성자 함수에서 직접 접근해야 실행이 가능하다.

var arr = [1, 2];
arr.forEach(function () {}); // 가능
Array.isArray(arr); // 가능
arr.isArray(); // 불가능


1-2 constructor 프로퍼티

생성자 함수의 프로퍼티인 prototype객체 내부에는 constructor라는 프로퍼티가 있다. 이 프로퍼티는 생성자 함수 자신을 가리킨다.

constructor 프로퍼티

var arr = [1, 2];
Array.prototype.constructor === Array; // true
arr.__proto__.constructor === Array; // true
arr.constructor === Array; // true

var arr2 = new arr.constructor(3, 4);
console.log(arr2); // [3, 4]

인스턴스의 __proto__가 생성자 함수의 prototype프로퍼티를 참조하며 __proto__가 생략 가능하기 때문에 인스턴스에서 직접 constructor에 접근할 수 있는 수단이 생긴 것이다.


한편, constructor는 읽기 전용 속성이 부여된 예외적인 경우(기본형 리터럴 변수 - Number, String, Boolean)를 제외하고는 값을 바꿀 수 있다.

constructor 변경

var NewConstructor = function () {
  console.log("this is new constuctor!");
};
var dataTypes = [
  1, // Number & false
  "test", // String & false
  true, // Boolean & false
  {}, // NewConstructor & false
  [], // NewConstructor & false
  function () {}, // NewConstructor & false
  /test/, // NewConstructor & false
  new Number(), // NewConstructor & false
  new String(), // NewConstructor & false
  new Boolean(), // NewConstructor & false
  new Object(), // NewConstructor & false
  new Array(), // NewConstructor & false
  new Function(), // NewConstructor & false
  new RegExp(), // NewConstructor & false
  new Date(), // NewConstructor & false
  new Error(), // NewConstructor & false
];

dataTypes.forEach(function (d) {
  d.constructor = NewConstructor;
  console.log(d.constructor.name, "&", d instanceof NewConstructor);
});
  • 모든 데이터가 d instanceof NewConstructor명령에 대해 false를 반환한다.

constructor를 변경하더라도 참조하는 대상이 변경될 뿐, 이미 생성된 인스턴스의 원형이나 데이터 타입이 변하는 것은 아니다. 즉, 어떤 인스턴스의 생성자 정보를 알아내기 위해 constructor프로퍼티에 의존하는 것이 항상 안전한 것은 아니라는 것이다.


추가 예제를 통해 한번 더 정리해보자.

var Person = function (name) {
  this.name = name;
};
var p1 = new Person("사람1"); // Person { name: "사람1" } true
var p1Proto = Object.getPrototypeOf(p1);
var p2 = new Person.prototype.constructor("사람2"); // Person { name: "사람2" } true
var p3 = new p1Proto.constructor("사람3"); // Person { name: "사람3" } true
var p4 = new p1.__proto__.constructor("사람4"); // Person { name: "사람4" } true
var p5 = new p1.constructor("사람5"); // Person { name: "사람5" } true

[p1, p2, p3, p4, p5].forEach(function (p) {
  console.log(p, p instanceof Person);
});

p1부터 p5까지는 모두 Person의 인스턴스이다. 따라서, 다음 두 공식이 성립한다.

  1. 다음 각 줄은 모두 동일한 대상을 가리킨다.
[constructor][instance].__proto__.constructor[instance].constructor;
Object.getPrototypeOf([instance]).constructor[Constructor].prototype
  .constructor;
  1. 다음 각 줄은 모두 동일한 객체(prototype)에 접근할 수 있다.
[Constructor].prototype[instance].__proto__[instance];
Object.getPrototypeOf([instance]);



2. 프로토타입 체인

2-1 메서드 오버라이드

prototype객체를 참조하는 __proto__를 생략하면 인스턴스는 prototype에 정의된 프로퍼티나 메서드를 마치 자신의 것처럼 사용할 수 있다고 했다. 그런데 만약 인스턴스가 동일한 이름의 프로퍼티 또는 메서드를 가지고 있는 상황이라면 어떨까?

메서드 오버라이드

var Person = function (name) {
  this.name = name;
};
Person.prototype.getName = function () {
  return this.name;
};

var iu = new Person("지금");
iu.getName = function () {
  return "바로 " + this.name;
};
console.log(iu.getName()); // 바로 지금
  • iu인스턴스의 getName메서드를 호출하면 Person.prototype에 정의된 getName이 아닌 iu인스턴스에 정의된 getName이 호출된다.

여기서 일어난 현상을 메서드 오버라이드라고 하는데, 메서드 위에 메서드를 덮어씌웠다는 표현이다.

__proto__에 있는 메서드는 자신에게 있는 메서드보다 검색 순서에서 밀려 호출되지 않은 것이다.


그렇다면 메서드 오버라이딩이 이뤄져 있는 상황에서 prototype에 있는 메서드에 접근하려면 어떻게 해야 할까?

console.log(iu.__proto__.getName()); // undefined
  • thisrk prototype객체 (iu.__proto__)를 카리키는데, prototype상에는 name프로퍼티가 없으므로 undefined가 반환된다.
Person.prototype.name = "이지금";
console.log(iu.__proto__.getName()); // 이지금
  • prototype에 name을 추가하니 원하는 메서드(prototype에 있는 getName)가 호출된다.

다만, this가 prototype을 바라보고 있으니 인스턴스를 바라보게 하려면 call이나 apply를 사용해야 한다.

console.log(iu.__proto__.getName.call(iu)); // 지금


일반적으로 메서드가 오버라이드된 경우에는 자신으로부터 가장 가까운 메서드에만 접근할 수 있지만, 이처럼 우회적인 방법을 통해 그다음으로 가까운 __proto__에 있는 메서드에도 접근할 수 있다.


2-2 프로토타입 체인

프로토타입 체인을 설명하기에 앞서 객체와 배열의 내부 구조를 살펴보자.

console.dir({ a: 1 });


console.dir([1, 2]);

객체와 달리 배열에서는 __proto__안에 또다시 __proto__가 있는데, 이는 prototype객체가 '객체'이기 때문이다. 기본적으로 모든 객체의 __proto__에는 Object.prototype이 연결된다.

그림으로 나타내면 다음과 같다.

이미지 출처: "코어 자바스크립트", 정재남, 위키북스, 2019, p. 164.

__proto__는 생략 가능하다고 했다. 그렇기 때문에 배열이 Array.prototype내부의 메서드를 마치 자신의 것처럼 실행할 수 있었다. 마찬가지로 Object.prototype내부의 메서드도 자신의 것처럼 실행할 수 있다. 생략 가능한 __proto__를 한번 더 따라가면 Object.prototype을 참조할 수 있기 때문이다.


배열에서 배열 메서드 및 객체 메서드 실행

var arr = [1, 2];
arr.push(3);
arr.hasOwnProperty(2); // true

이처럼 어떤 데이터의 __proto__프로퍼티 내부에 다시 __proto__프로퍼티가 연쇄적으로 이어진 것을 프로토타입 체인이라고 하며, 이 체인을 따라가며 검색하는 것을 프로토타입 체이닝이라고 한다.

프로토타입 체이닝은 메서드 오버라이드와 동일한 맥락으로, 어떤 메서드를 호출했을 때 자신의 프로퍼티에 해당 메서드가 없다면 __proto__를 따라가며 검색하는 식으로 진행된다.


메서드 오버라이드와 프로토타입 체이닝

01 var arr = [1, 2];
02 Array.prototype.toString.call(arr); // 1,2
03 Object.prototype.toString.call(arr); // [object Array]
04 arr.toString(); // 1,2
05
06 arr.toString = function() {
07   return this.join('_');
08 };
09 arr.toString(); // 1_2
  • arr변수는 배열이므로 arr.__proto__는 Array.prototype을 참조하고, Array.prototype은 객체이므로 Array.prototype.__proto__는 Object.prototype을 참조한다.
  • 4번째 줄에서 arr.toString을 실행하면 Array.prototype.toString을 적용한 것과 같은 결과가 나온다.
  • 6번째 줄에서는 arr에 직접 toString메서드를 부여했다. 이로써 9번째 줄에서는 Array.prototype.toString이 아닌 arr.toString이 바로 실행된다.


배열뿐만 아니라, 자바스크립트 데이터는 모두 아래와 같이 동일한 형태의 프로토타입 체인 구조를 지닌다.

이미지 출처: "코어 자바스크립트", 정재남, 위키북스, 2019, p. 166.

그림의 위쪽 삼각형의 우측 꼭짓점에는 무조건 Object.prototype이 위치하며, 삼각형이 꼭 두 개만 연결되는 것은 아니다.


추가적으로 중요도가 낮은 내용을 하나 언급해보려는데, 프로토타입을 처음 접하는 경우에는 혼란스러울 수 있으니 이 부분은 건너뛰고 학습해도 무방하다.

앞서 소개한 도식의 삼각형들은 오직 instance를 중심으로 __proto__를 따라가는 루트만 표기헀는데, 접근 가능한 모든 경우를 표기하면 다음 그림처럼 복잡한 구조가 되어버린다.

이미지 출처: "코어 자바스크립트", 정재남, 위키북스, 2019, p. 167.

각 생성자 함수는 모두 함수이기 때문에 Function생성자 함수의 prototype과 연결되고, Function생성자 함수 역시 함수이므로 다시 Function생성자 함수의 prototype과 연결된다. 이런식으로 __proto__의 constructor의 __proto__의 constructor...를 재귀적으로 반복하는 루트를 따르면 끝없이 찾아갈 수 있다. 실제 메모리 상에서 데이터를 무한대의 구조 전체를 들고 있는 것은 아니고, 사용자가 이런 루트를 통해 접근하고자 할 때 비로소 해당 정보를 얻을 수 있을 뿐이다.

instance.constructor.constructor이든 instance.constructor.constructor.constructor이든 결국 같은 Function생성자 함수를 가리키므로 메모리가 낭비될 이유는 없다. 뿐만 아니라, 이런 접근은 그 자체로 별다른 의미가 없다. 이미 생성자 함수를 알고 있는 이상, 어떤 인스턴스가 해당 생성자 함수의 인스턴스인지 여부를 알아야 하는 경우가 아니라면 인스턴스를 통해 접근할 것 없이 그냥 생성자 함수를 사용하면 되기 때문이다.

따라서, 일반적으로 인스턴스와 "직접적인 연관"이 있는 삼각형에만 주목하면 된다.


2-3 객체 전용 메서드의 예외사항

자바스크립트의 모든 객체는 프로토타입 체인을 통해 Object.prototype에 연결된다. 이는 Object.prototype이 모든 객체의 최상위 프로토타입으로 존재한다는 것을 의미한다.

따라서, Object.prototype에 추가된 메서드는 모든 객체가 접근할 수 있는 범용적인 메서드가 된다. 그러나, Object.prototype에 메서드를 추가하는 것은 예상치 못한 부작용을 초래할 수 있다.

다음의 코드를 살펴보자.

Object.prototype에 추가한 메서드에의 접근

01 Object.prototype.getEntries = function() {
02   var res = [];
03   for (var prop in this) {
04     if (this.hasOwnProperty(prop)) {
05       res.push([prop, this[prop]]);
06     }
07   }
08   return res;
09 };
10 var data = [
11   ['object', { a: 1, b: 2, c: 3 }], // [["a",1], ["b", 2], ["c",3]]
12   ['number', 345], // []
13   ['string', 'abc'], // [["0","a"], ["1","b"], ["2","c"]]
14   ['boolean', false], // []
15   ['func', function() {}], // []
16   ['array', [1, 2, 3]], // [["0", 1], ["1", 2], ["2", 3]]
17 ];
18 data.forEach(function(datum) {
19   console.log(datum[1].getEntries());
20 });
  • 객체만 사용할 의도로 getEntries라는 메서드를 만들었다.
  • 18번째 줄의 forEach에 따라 11번째 줄부터 16번째 줄의 각 데이터마다 getEntries메서드를 실행한다.
  • 의도와 다르게 모든 데이터가 오류 없이 결과를 반환한다.


의도와 다르게 동작하는 것은 프로토타입 체이닝을 통해 모든 데이터 타입에서 getEntries메서드에 접근할 수 있기 때문이다.

위와 같은 이유로 객체 전용 메서드들은 Object.prototype이 아닌 Object에 스태틱 메서드로 정의된다. 이러한 스태틱 메서드들은 객체에만 적용되며, Object와 인스턴스인 객체 리터럴 사이의 this 연결을 통한 접근이 불가능하다. 대신, 대상 객체를 메서드의 인자로 직접 전달하는 방식으로 구현되어 있다. 예를 들어, Object.freeze(instance)와 같은 형태로 사용된다.

만약, 객체 전용 메서드들을 Object.prototype에 부여할 수 있었다면, instance.freeze()와 같은 형태로 인스턴스 메서드처럼 사용할 수 있었을 것이다. 그러나, 이는 Object.prototype이 모든 데이터 타입에서 접근 가능한 최상위 프로토타입이기 때문에 불가능하다.

같은 이유로 Object.prototype에는 toString, hasOwnProperty, valueOf, isPrototypeOf와 같이 모든 변수에서 호출할 수 있는 보다 범용적인 메서드들만 존재한다. 즉, 모든 변수가 마치 자신의 메서드인 것처럼 호출할 수 있다.


앞서 '프로토타입 체인상 가장 마지막에는 언제나 Object.prototype이 있다'고 했는데, 예외적으로 Object.create를 이용하면 Object.prototype의 메서드에 접근할 수 없는 경우가 있다. Object.create(null)은 __proto__가 없는 객체를 생성한다.

var _proto = Object.create(null);
_proto.getValue = function (key) {
  return this[key];
};
var obj = Object.create(_proto);
obj.a = 1;
console.log(obj.getValue("a")); // 1
console.dir(obj);

_proto에는 __proto__프로퍼티가 없는 객체를 할당했다. 다시 obj를 출력해보면 __proto__에는 오직 getValue메서드만이 존재하며, __proto__ 및 constructor프로퍼티 등은 보이지 않는다. 이 방식으로 만든 객체는 일반적인 데이터에서 반드시 존재하던 내장(built-in)메서드 및 프로퍼티들이 제거됨으로써 기본 기능에 제약이 생긴 대신, 객체 자체의 무게가 가벼워짐으로써 성능상 이점을 가진다.


2-4 다중 프로토타입 체인

자바스크립트의 기본 내장 데이터 타입들은 모두 프로토타입 체인이 1단계(객체) 또는 2단계(나머지)로 끝나지만, 사용자가 새롭게 만드는 경우에는 그 이상도 가능하다.

프로토타입 체인을 연결하는 방법은 __proto__가 가리키는 대상, 즉 생성자 함수의 prototype이 연결하고자 하는 상위 생성자 함수의 인스턴스를 바라보게 하는 것이다.

Grade 생성자 함수와 인스턴스

var Grade = function () {
  var args = Array.prototype.slice.call(arguments);
  for (var i = 0; i < args.length; i++) {
    this[i] = args[i];
  }
  this.length = args.length;
};
var g = new Grade(100, 80);
  • Grade 생성자 함수를 만들고, Grade의 인스턴스 g를 생성한다.

g는 배열의 형태를 지니지만, 배열의 메서드를 사용할 수 없는 유사배열객체이다. g에서 배열 메서드를 사용할 수 있게 하기 위해서는 g.__proto__, 즉 Grade.prototype이 배열의 인스턴스를 바라보게 하면 된다.

Grade.prototype = [];

이 명령에 의해 다음과 같이 서로 분리되어 있던 데이터가 연결되어 하나의 프로토타입 체인 형태를 띠게 된다.

이미지 출처: "코어 자바스크립트", 정재남, 위키북스, 2019, p. 172.


이제는 Grade의 인스턴스인 g에서 직접 배열의 메서드를 사용할 수 있다.

console.log(g);  // Grade(2) [100, 80]
g.pop();
console.log(g);  // Grade(1) [100]
g.push(90);
console.log(g);  // Grade(2) [100, 90]

g인스턴스의 입장에서는 프로토타입 체인에 따라 g객체 자신이 지니는 멤버, Grade의 prototype에 있는 멤버, Array.prototype에 있는 멤버, 끝으로 Object.prototype에 있는 멤버에까지 접근할 수 있게 됐다.



3. 정리

어떤 생성자 함수를 new연산자와 함께 호출하면 Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스가 생선되는데, 이 인스턴스에는 __proto__라는 Constructor의 prototype프로퍼티를 참조하는 프로퍼티가 자동으로 부여된다. __proto__는 생략 가능한 속성이라서, 인스턴스는 constructor.prototype의 메서드를 마치 자신의 메서드인 것처럼 호출할 수 있다.

Constructor.prototype에는 constructor라는 프로퍼티가 있는데, 이는 다시 생성자 함수 자신을 가리킨다. 이 프로퍼티는 인스턴스가 자신의 생성자 함수가 무엇인지를 알고자 할 때 필요한 수단이다.

직각삼각형의 대각선 방향, 즉 __proto__방향을 계속 찾아가면 최종적으로는 Object.prototype에 당도하게 된다. 이런 식으로 __proto__를 찾아가는 과정을 프로토타입 체이닝이라고 하며, 이 프로토타입 체이닝을 통해 각 프로토타입 메서드를 자신의 것처럼 호출할 수 있다. 이때 접근 방식은 자신으로부터 가장 가까운 대상부터 점차 먼 대상으로 나아가며, 원하는 값을 찾으면 검색을 중단한다.

Object.prototype에는 모든 데이터 타입에서 사용할 수 있는 범용적인 메서드만이 존재하며, 객체 전용 메서드는 여느 데이터 타입과 달리 Object생성자 함수에 스태틱하게 담겨있다.

프로토타입 체인은 반드시 2단계로만 이뤄지는 것이 아니라 무한대의 단계를 생성할 수도 있다.

코어 자바스크립트

Part 4 of 14

이 시리즈에서는 '코어 자바스크립트'를 통해 자바스크립트의 근간을 이루는 핵심 이론들을 정확하게 이해하는 것을 목표로 합니다. 기본 이론들 중에서 ES5와 ES6를 아우르는 중요성이 높은 핵심 개념을 위주로 다루며, 테크닉이나 요령보다는 원리를 이해하는 데 목적을 두고 학습합니다.

Up next

[5장] 클로저 - 북 스터디

코딩 마을 방범대 북 스터디 DAY 5

More from this blog

💥 new Array(length).map()으로 배열 초기화 시 콜백이 실행되지 않는 문제

JavaScript에서 배열을 초기화하려고 new Array(4).map(() => [])을 사용했는데 의도대로 동작하지 않았다. 원인을 찾아보니 JavaScript의 빈 슬롯과 undefined의 차이, 그리고 map() 메서드가 빈 슬롯에 대해 콜백을 실행하지 않는다는 특성때문이었다. 문제 상황 특정 크기의 배열을 만들고 각 요소를 []로 초기화 하려고 했다. const result = new Array(4).map(() => []); co...

Jun 28, 20252 min read17

💥 에러 메시지와 스택 트레이스 분석을 통해 문제 해결하기

JavaScript로 카드 게임 로직을 구현하던 중 TypeError: Cannot read properties of undefined라는 에러를 만났다. 코드가 복잡하고 짐작가는 부분이 단번에 떠오르지 않아 막막했는데, 에러 메세지와 스택 트레이스를 차근차근 분석해가며 원인을 찾아낸 과정을 기록해본다. 에러 발생 게임 로직을 테스트하던 중 특정 입력에서 다음과 같은 에러가 발생했다. TypeError: Cannot read properties...

Jun 25, 20254 min read11

🧐 적절한 함수명과 단일 책임으로 코드 개선하기

이 글에서는 JavaScript로 구현된 보드게임의 로직을 개선하는 과정을 다루며, 개선 과정은 함수명 개선 → 중복 제거 → 책임 분리의 단계로 이루어져있다. 작동하지만 개선이 필요한 코드 처음에 작성한 코드는 기능적으로는 문제없이 동작했지만, 더 나은 방식을 고민하다 보니 여러 개선점을 찾을 수 있었다. function checkLadder(position) { switch (position) { case 4: return 14;...

Jun 23, 20253 min read23

작심삼십일 - TypeScript 강의를 완강하다.

유데미 작심삼십일 챌린지에 참여하다 작심삼십일 챌린지는 유데미의 한국 런칭 3주년을 기념한 이벤트 중 하나로, 30일 동안 유데미 강의를 들으며 인증을 하는 챌린지이다.취업 준비를 하면서 여러가지 요인으로(주로 심리적이지만..) 학습 루틴이 망가졌던 찰나에 이 이벤트를 보게되었고, 마침 기존에 사두고 볼 엄두가 안 났던 타입스크립트 강의가 있었기에 옳다구나! 하고 신청했었다. 챌린지를 통해 무엇을 배웠나? 챌린지의 목표로 정한 강의는 유데미에...

Nov 18, 20242 min read58
작심삼십일 - TypeScript 강의를 완강하다.

woodstock's devlog

215 posts

안녕하세요! 프론트엔드 개발자 woodstock입니다.

제 블로그에서는 프론트엔드 개발에 대한 다양한 주제를 다룹니다.

JavaScript, TypeScript, React, Next.js 등의 기술을 활용하여 실제 프로젝트를 진행하면서 얻은 경험과 지식을 공유하고자 하며, 컴퓨터 과학(CS)의 기본 개념과 알고리즘, 자료구조 등에 대해서도 다룹니다.