들어가기 전에

자바스크립트 개발자라면 알아야 할 33가지 개념 #16 자바스크립트 : 'new' 연산자

저자의 공식 사이트 주소는 codeburst.io입니다. 만일 질문이 있다면 트위터 @brandonmorelli에서 하면 된다고 합니다. 저자의 코스강의도 수강해보세요.

4가지 규칙

new 연산자를 이해하는 가장 쉬운 방법은 new연산자가 무엇을 하는지 이해하는 것입니다. new를 사용할 때, 다음 4가지 일이 벌어집니다.

  1. 새로운 빈 오브젝트를 생성합니다.
  2. this를 새롭게 생성된 오브젝트에 바인드 시킵니다.
  3. 새롭게 생성된 오브젝트의 프로퍼티에 "proto" 라고 불리는 생성자 함수의 프로토타입 오브젝트를 추가합니다.
  4. 함수에서 완성된 오브젝트가 반환될 수 있도록, return this를 함수의 맨 마지막 부분에 추가합니다.

잠깐만요. 뭐라구요?

아직 위의 규칙들이 잘 이해되지 않아도 괜찮습니다. 간단한 것부터 시작합시다. 생성자 함수 Student를 만들어봅시다. 이 함수는 2개의 파라미터 name, age를 갖습니다. 이 함수는 이 프로퍼티를 this의 값에 세팅할 것입니다.

function Student(name, age) {
  this.name = name;
  this.age = age;
}

완벽합니다. 이제 우리 생성자 함수를 new 연산자로 호출해봅시다. 우리는 'John'26를 인자로 전달할 겁니다.

var first = new Student('John', 26);

위 코드를 실행시켰을 때, 무슨 일이 일어났을까요?

  1. 새로운 오브젝트가 만들어졌습니다 - first 오브젝트가 만들어졌습니다.
  2. this가 우리의 first 오브젝트에 바운딩 됐습니다. 그래서 this를 참조하면 first 오브젝트가 참조됩니다.
  3. proto가 추가됐습니다. first.__proto__는 이제 Student.prototype을 가리킬 겁니다.
  4. 모든 것이 끝난 뒤에, 새로운 first 오브젝트가 리턴되어 first 변수에 할당되었습니다.

이제 우리는 이 과정이 제대로 작동하는지 테스트하기 위해, 몇가지 간단한 console.log를 실행시킬 수 있습니다.

console.log(first.name);
// John
console.log(first.age);
// 26

훌륭합니다. new 키워드의 __proto__ 부분에 더 빠져봅시다.

프로토타입(Prototypes)

모든 자바스크립트 오브젝트는 프로토타입을 가지고 있습니다. 자바스크립트의 모든 오브젝트는 프로토타입에서 메소드를 상속받고 프로퍼티를 상속받습니다.

예제를 확인해봅시다. 구글 개발자도구 콘솔을 열고 Student 함수를 타이핑해봅시다.

function Student(name, age) {
  this.name = name;
  this.age = age;
}

모든 오브젝트가 프로토타입을 가진다는 것을 증명하기 위해, 다음을 타이핑해봅시다.

Student.prototype;
// Object {...}

좋습니다. 오브젝트가 반환되었습니다. 이제 새로운 student를 만들어봅시다.

var second = new Student('Jeff', 50);

마침내, 우리의 Student.prototype.constructor가 우리 Student 생성자 함수를 가리키고 있음을 알게 됐습니다.

Student.prototype.constructor;
//  function Student(name, age) {
//    this.name = name;
//    this.age = age;
//  }

매우 빨리 복잡하게 변합니다. 엉터리 그림판 그림이 무엇이 일어나는지 설명해줄 겁니다.

crappypaint.png

정말 아름답죠?

위에서 볼 수 있듯이, 우리의 Student 생성자 함수는 (모든 생성자 함수들도 마찬가지) .prototype이라 불리는 프로퍼티를 가집니다. 이 프로토타입은 프로퍼티에 .constructor라 불리는 오브젝트를 가지고 있습니다. 이 오브젝트는 생성자 함수를 다시 가리킵니다. 훌륭한 자그마한 루프 입니다. 후에 우리가 new 연산자를 이용하여 새로운 오브젝트를 만들 때, 각각의 오브젝트는 .__proto__ 프로퍼티를 갖습니다. __proto__ 프로퍼티는 새로운 오브젝트를 다시 Student.prototype 으로 연결해줍니다.

그래서 이게 왜 중요할까요?

상속 때문에 중요합니다. 프로토타입 오브젝트는 그 생성자 함수로 만들어진 모든 오브젝트에서 공유됩니다. 이 말은 우리가 함수나 프로퍼티를 프로토타입에 추가하면 모든 오브젝트가 그것들을 이용할 수 있다는 말입니다.

위의 예제에서는 우리는 2개의 Student 오브젝트만 만들었습니다. 하지만 2개의 오브젝트가 아니라 20,000개쯤 만들었으면 어땠을까요? 그랬다면 우리는 각각의 오브젝트에 함수를 넣는 대신 공유하는 함수들을 프로토타입에 넣음으로써 엄청난 프로세싱 파워를 절약했을 것입니다.

이 아이디어를 완벽히 숙지하기 위해 다음 예제를 봅시다. 콘솔에 다음 코드를 추가해보세요.

Student.prototype.sayInfo = function(){
  console.log(this.name + ' is ' + this.age + ' years old');
}

다시 설명하자면, 우리가 지금 입력한 것은 Student 프로토타입에 함수를 추가한 것입니다. 우리가 생성한 student 오브젝트나 이미 생성된 student 오브젝트 모두 새로운 메소드인 .sayInfo에 접근할 수 있습니다. 그럼 한번 테스트 해봅시다.

second.sayInfo();
// Jeff is 50 years old

새로운 student 오브젝트를 추가하고, 다시 한번 테스트해봅시다.

var third = new Student('Tracy', 15);
// Now if we log third out, we see the object only has two
// properties, age and name. Yet, we still have access to the 
// sayInfo function:
third;
// Student {name: "Tracy", age: 15}
third.sayInfo();
// Tracy is 15 years old

잘 작동합니다! 그리고 이게 잘 작동하는 이유는 상속(inheritance) 때문입니다. 자바스크립트 오브젝트는 처음에 우리가 호출하는 프로퍼티가 있는지부터 검사할 것입니다. 만일 우리가 호출하는 프로퍼티가 없다면, 상위로 올라갑니다. 상위에는 프로토타입이 있고 자바스크립트 엔진은 *"헤이! 너 이 프로퍼티 있니?* 라고 물어볼 것입니다. 이 패턴은 그 프로퍼티를 찾을 때까지 계속됩니다. 다만 전역 오브젝트에서 프로토타입 체인은 끝나게 됩니다.

상속은 과거에 .toString()과 같은 메소드를 사용할 수 있었던 것과 같은 원리입니다. 생각해보세요. 우리는 .toString() 메소드를 전혀 코딩한적이 없습니다. 하지만 잘 써왔습니다. 그 이유는 다른 자바스크립트 메소드가 Object prototype에 이미 작성되어 있기 때문입니다. 우리가 만드는 모든 오브젝트는 궁극적으로 Object prototype를 상속받게 되어 있습니다. 그리고 우리는 이러한 메소드를 오버라이딩 할 수도 있습니다. 다음과 같이요.

var name = {
  toString: function(){
    console.log('Not a good idea');
  }
};
name.toString();
// Not a good idea

우리의 오브젝트는 처음에 프로토타입으로 이동하기 전에 오브젝트 내부에 메소드가 있는지 먼저 확인합니다. 우리 오브젝트가 메소드를 갖고 있기 때문에, 상속이 일어나지 않고 우리 오브젝트가 가진 메소드가 작동합니다. 하지만 이건 좋은 아이디어는 아닙니다. 글로벌 메소드를 그냥 내버려 두세요. 메소드의 이름을 지을 때는 글로벌 메소드의 이름을 피하세요.

결론

새로운 개발자로써, 이건 매우 이해하기 어려운 개념일지도 모릅니다. 하지만 일단 이해하면, 훨씬 군더더기 없고 나은 코드를 작성할 수 있습니다. 프로토타입과 함께 우리는 수백 수천개의 오브젝트에서 중복되는 코드를 매우 빠르고 효율적으로 공유할 수 있습니다. 저의 다른 모든 아티클같이, 이 아티클의 아이디어는 단지 여러분이 기본적인 개념만 익힐 수 있도록 해줄 뿐입니다. 스스로 더 많이 찾아보시는 편이 도움이 될 겁니다.