객체…. 객체 ?

-본 내용은 코드스쿼드 crong마스터의 강의를 들은 뒤 정리한 것임을 밝히고 시작합니다.

기초지식
  1. object literal & Encapsulation
  • object literal 이란?
    key와 value값을 가진 형태의 값을 말합니다.
  • Encapsulation이란?
    캡슐화를 말합니다.

2. call & this

  • call?
    call 함수는 함수를 호출할 때, 호출되는 함수에 this가 가리키는 참조를 바꿀 수 있는 능력이 있습니다.
  • this?
    this는 현 객체를 참조합니다. 어느곳의 객체에도 속해있지 않다면, this는 아무것도 아니어야 맞습니다. window를 반환해주기는 하지만, 그것은 잘못된 일입니다.

3. new

  • new?
    선언할 때, 생성자함수(constructor) 가 this를 리턴 해주며, 객체 내에 있는 모든 메서드들이 프로토타입 객체 내로 들어가게 만들어줍니다.

4. constructor

  • constructor?
    생성자 함수 입니다. 생성자 함수 내의 값으로 객체의 속성을 만들어 줄 수도 있고, this를 리턴해줍니다.

5. prototype

  • prototype?
    설명수 있는 것들이 많지만, 우선 모든 함수에 존재한다고만 알고 있도록 합시다.

6. __proto__

  • __proto__?
    __proto__는 객체가 생성될 때 조상 이었던 함수의 prototype을 가리킵니다.

7. Object.getPrototypeOf

  • getPrototypeOf?
    getPrototypeOf는 객체의 프로토타입을 얻을 수 있습니다.

8. Object.prototype (root prototype)

  • Object.prototype?
    Object.prototype은 모든 prototype의 뿌리입니다. prototype을 타고올라가다보면, 결국에는 Object.prototype의 객체가 나오게 되는것이죠.

9. Object.create

  • Object.create??
    Object.create는 아시다시피 객체를 만들어주는 메서드입니다.

10. Es Classes

  • ES Classes?
    이것에 대해서도 설명할 거리가 많지만, 우선 constructor내의 값으로 속성을 만들 수 있고, class내의 함수로 그 객체가 가지는 프로토타입의 메서드를 만들 수 있다고만 알아 둡시다.

11. own properties -> own prototype -> prototype… > Object.prototype

  • 이게뭔소리야?
    own properties -> ownprototype -> prototype… > Object.prototype =>값을 찾는 과정입니다. 자신의 속성 -> 그자신의 프로토타입 -> 그다음의 프로토타입 …-> 최상위객체의 프로토타입
    순으로 진행됩니다.
primitive values(원시값)가 객체처럼 동작한다고?

참조가 아닌 값으로 평가되는 primitive values 들이 이렇게 동작하는 것을 우리는 알고 있습니다.

결과 값으로 3이 나옵니다. 여기서 참 이상한걸 알 수 있습니다. ‘2018’은 primitive value일텐데, 어찌하여 .indexOf()메서드를 사용할 수 있을까요? 2018 문자열이 객체라서 객체전용문자열을 사용할 수 있는 걸까요?

promitive value도 .이 찍히는 순간 객체처럼 변경되서 동작합니다. 이 문자열 값의 프로토타입 또한 Object.prototype까지 chaining 이 되어 있답니다.

JavaScript의 모든 타입은 객체이다! 라는 말이 여기서부터 파생되어온 말임을 알 수 있습니다.

prototype & constructor

다음의 결과는 무엇일까요?

답은 똑같은 foo.prototype.constructor입니다. 결론적으로 왔다갔다 하는것 밖에 되질 않죠.

객체 탐색

객체는 for — in 문을 통해서 열거할 수 있습니다.
하지만 아래의 코드를 보면 결과는 예상과는 다르게 동작하는 것을 볼 수 있습니다.

1. 객체 자신이 소유하고 있는 결과만 나오게 하려면 어떻게 수정해야할까요?

아래에 어떤 값을 추가하면 되는지와 여러가지 방법을 활용해서 객체를 탐색할 수 있는 방법을 적어보았습니다.

prototype 결합

JavaScript에서는 prototype을 통해 상속(Inheritance)을 흉내낼 수 있습니다. 
Prototype Inheritance를 돕는, Object.create 함수코드는 아래 URL을 참고해봅시다.
> code : https://crockford.com/javascript/prototypal.html

아래의 코드를 보고 bar 인스턴스의 prototype 결합구조를 구체적으로 설명 해봅시다.

여기서 bar인스턴스의 prototype은 Object.create(Foo.prototype)이 됩니다. (7번째라인) Object.create(Foo.prototype)은 비어있는 함수를 반환해주기 때문에 함수 Bar와 Foo의 prototype이 연결됩니다. 이것을 보고 prototypeChain이라고 합니다. 
 bar 인스턴스의 prototype결합구조를 구체적으로 설명하자면, bar인스턴스의 프로토 타입에는 Bar의 prototype인 getValue메서드가 존재하고 Bar.prototype을 Object.create(Foo.prototype)하였으니, Bar의 prototype내에는 Foo의 prototype이 들어가 Foo의 prototype속성인 value가 10이되는 것입니다. 그래서 bar.getValue()함수를 호출하게 되면 prototypeChaining 을 통해 10이라는 값이 도출됩니다.

자 여기서 두번재 질문. 위 코드에서 bar.constructor 는 왜 Foo를 가리킬까요? Bar를 가리키게 하려면 어떻게 해야할까요? 
 저 혼자 이것저것 만져보며 건드린 결과 Bar.prototype.constructor = Bar를 7번째 라인에 추가해주기만 하면 bar.constructor가 Bar를 가리키게 할 수 있었습니다. bar.constructor가 Foo를 가리키는 이유는 간단합니다. Bar.prototype을 Object.create(Foo.prototype)로 선언했기 때문입니다. (Foo.prototype에도 Foo.constructor가 들어가 있기 때문에 bar.constuctor는 Foo.prototype.constructor와 같습니다. Foo.prototype.constructor 는 Foo와 같고요.)

참고 : https://hackernoon.com/object-create-in-javascript-fa8674df6ed2

Inheritance

Inheritance는 상속을 말합니다. 아래의 코드를 한번 살펴봅시다.

Foo 생성자는, getMsg와 getMsgLength 메서드가 있습니다.
Foo는 template literal(``표시를 뜻합니다.)을 통해서 welcome 메시지와 함께 html 문자열을 반환하는 역할을 합니다.
 또한, 아래의 Bar는 Foo와 같이 동작하지만, 대문자로 만들고, 공백을 제거한 html 문자열을 반환합니다.
Bar도 getMsgLength메서드가 필요합니다. (실제로 쓸모는 없지만) 그리하여 Bar는 Foo와 유사한 점이 많아서, Foo를 상속하기로 결정 했다고 칠 때, 아래의 코드에서 
??? 로 된 부분에 코드를 추가해서 맨 밑의 결과값을 출력해 봅시다.

저희가 원하는 것은, ????부분과 //부모의….~ 부분에 어떠한 방법을 추가해서 foo의 메서드도 사용하고 Bar의 메서드도 사용하도록 하는 것입니다. 정답부터 말하자면, ??? 부분에는 Foo.call(this, msg)를 넣으면 되고, //부모의~ 부분에는 Foo.prototype.getMsg.call(this)를 넣으면 됩니다. 
 Foo.call(this, msg)를 넣으면 되는 이유는, 인자로 받는 ‘crong~crong~’을 call메서드를 이용해서 Bar의 속성으로 만들기 때문이고, bar.getMsg()를 호출하면, this.msg 는 소문자가 대문자로 바뀌고 공백이 사라지게된다. 그리고 그 메세지는 Foo.prototype.getMsg.call(this)를 통하여, Foo.prototype.getMsg()메서드내의 this.msg가 대문자 CRONG~CRONG~으로 바뀌게 되어 결과적으로 마지막 문장이 출력되는 것이다.

ES6 Classes 를 활용한 Inheritance

자 그럼 위의 Foo/Bar 생성자를 ES6 Classes로 마이그레이션 해볼까요?
대신 몇가지 조건이 있습니다.

  • Object.create 대신 extends 키워드 를 사용합니다.
  • prototype 객체를 직접 사용하지 않음.
  • - ES6의 super 키워드를 사용해서 부모의 생성자와, 부모의 prototype 메서드를 호출합니다.

Ex) 참고코드) MDN

정답은 아래와 같습니다. 하지만,

저희는 설명할 수 있어야합니다.

우선 constructor는 Foo함수와 같다는 것을 알 수 있을 것입니다. 그리고 클래스 내의 메서드들은 자동적으로 prototype내로 들어가니 문제가 없습니다. Bar를 만들때엔, extends라는 키워드를 사용하는데, 위의 참고사이트 MDN을 찾아보면, extend에 대해 잘 나와있습니다. extend를 사용하게 되면, Foo내에 있는 메서드가 Bar를 생성할때 prototype으로 들어오게 됩니다. 그리고 Bar내에서 부모의 메서드를 가지고 오고 싶다면, super를 사용하면 됩니다. super.getMsg()같이 말입니다.

글의 내용은 부족한 글쓴이의 지식으로 인해 틀린점이 많을 수 있습니다. 피드백은 언제나 환영합니다.