참고 자료
https://youtu.be/wUgmzvExL_E
https://youtu.be/ddJcDZHBRm0

프로토타입이란?

프로토타입은 원형, 즉, 원래의 형태를 의미한다.
원형을 의미한다는 말만 봤을 때는 단번에 '아 프로토타입이란 이거군.' 하고 파악할 수 없다.

이해한대로 간단하게 설명하면 자바스크립트는 객체 기반의 스크립트 언어이고 프로토타입은 자바스크립트를 이루고 있는 객체들이 가지는 고유한 객체를 의미한다.

예를 들어 우리가 Array 함수를 쓸 때 sort() 함수를 정의하지 않고도 Array.sort() 이렇게 자연스럽게 호출하여 사용할 수 있는 것은 Array가 가지는 고유한 객체인 프로토타입을 '상속' 받아 그 안의 프로퍼티인 sort()에 접근 가능하기 때문이다.
(보통 array 선언 방식이 let arr = [values...] 이지만 실제 컴퓨터가 선언하는 방식은 let arr = new arr(values...) 형식으로 객체를 생성한다.)

프로토타입은 위에서 언급된 '상속'과 밀접한 관련이 있다.

자바스크립트에서 함수는 객체다! 객체이기 때문에 속성(프로퍼티)들을 가질 수 있다.

자바스크립트에서 클래스 문법을 사용할 때 함수로 지정해둔 기본 객체 속성들을 new 키워드를 사용해 새로운 객체를 생성하여 그대로 갖다 쓸 수 있다.

ex)

<script>

  function hamburger () {
      this.a = 'bread';
      this.b = 'patty';
      this.c = 'sauce';
      this.d = 'lettuce';
      this.e = 'cheese';
      this.f = 'pikle';
      this.g = 'onion';
  }

  let mac = new hamburger();

</script>

위의 예시로 선언 시 mac을 콘솔창에 출력하면 hamburger function안에 선언된 속성들이 모두 출력되는 것을 볼 수 있다.

hamburger.prototype 을 출력하면 hamburger 함수가 갖고 있는 고유한 객체인 prototype을 볼 수 있다.
이 prototype에 hamburger.prototype.h = 'salt' 를 입력하여 h라는 키에 'salt' value를 추가한다면?
이 hamburger를 상속받은 객체가 저장된 mac 변수에서도 h라는 속성을 볼 수 있다.

예시를 좀 더 들어보자.
모든 array에서 사용 가능한 함수를 만들고 싶다면?

<script>

  Array.prototype.함수 = function() { 구현 ~ };

  let arr = [values...];

  arr.함수();

</script>

위처럼 정의해보자. 이제 모든 Array에서는 위에 정의된 '함수'라는 함수를 사용할 수 있는 것이다.

위처럼 프로토타입에 속성을 추가하면 자식객체는 그 추가된 속성을 갖지 않고 부모객체만 추가된 속성을 갖게된다.

이렇게 자식객체(mac)은 갖고 있지 않고 부모객체(hamburger)만 갖고 있는 속성을 쓸 수 있는 원리는?
프로토체인 덕분이다.


프로토체인이란?

객체에서 어떤 값을 불러올 때 (ex) mac.a) 해당 값이 호출한 객체에 없으면 해당 객체의 부모객체의 프로토타입에서 찾아본다.
부모객체에도 없으면 부모의 부모 객체의 프로토타입에서 찾아본다.
이렇게 객체에서 원하는 값에 접근할 때 부모객체를 상속 받은 프로토타입을 계속하여 탐색하는 것을 프로토체인이라고 한다.


__proto__ 문법

prototype을 사용하는 문법에는 위와같이 객체.prototype 문법과 __proto__를 이용하는 문법이 있다.

<script>

  const car = {
      wheels : 4,
      drive() {
          console.log("drive..");
      }
  }

  const bmw = {
      color: "red",
      navigation: 1,
  };

  const benz = {
      color: "black"
  };

  const audi = {
      color: "blue"
  };

  bmw.__proto__ = car;
  benz.__proto__ = car;
  audi.__proto__ = car;
  
 </script>

위의 객체들 중 car에 있는 wheels와 drive() 속성은 각 객체에 공통으로 들어가는 속성이다.
반복적인 작성을 피하기 위해서 프로토타입을 이용할 수 있다.
상위 객체로 car를 만들어주고 이 car객체를 bmw, benz, audi의 프로토타입을 선언한다. (bmw.__proto__ = car; 이부분)
즉, bmw, benz, audi는 car를 상속받는 것이다.

bmw.wheels 를 출력하면 bmw 내부의 속성을 찾다가 wheels라는 속성이 없기 때문에 __proto__(프로토타입)을 확인한다.
즉, __proto__프로토타입(car를 상속받음) 안에서 wheels 속성을 찾아서 값을 가져오게 된다.

여기서 만약 또 다른 객체가 생성되고 그 새로운 객체가 '객체.__proto__=bmw' 이렇게 상속 받는다면?
객체.wheels 이렇게 새로운 객체에서 wheels를 호출 시
1.'객체'라는 객체를 탐색wheels속성이 없으니까
2. bmw를 상속받은 프로토타입을 탐색, 또 없으니까
3. bmw가 상속 car를 상속받아 갖고 있는 프로토타입을 탐색

이렇게 순차적으로 해당 속성을 갖고 있는 프로토타입을 탐색하게 된다. => 프로토체인!

위 문법을

<script>

	const Bmw = function (color) {
    		this.color = color;
   	 };

        Bmw.prototype.wheels = 4;
        Bmw.prototype.drive = function () {
            console.log("drive..");
        }

        const x5 = new Bmw("red");
        const z4 = new Bmw("blue");
  
 </script>

이렇게 작성하면 생성자 (new Bmw())를 통해서 생성하는 객체에 __proto__를 wheels(Bmw.prototype.wheels 이부분), drive(Bmw.prototype.drive 이부분) 로 설정한다는 것을 의미한다.

prototype 문법을 이용하면 개별적으로 __proto__를 설정할 필요 없이 일괄적으로 프로토타입 설정이 가능하다.
(__proto__는 객체별로 프로토타입을 하나하나 생성하는 느낌이다)

만약 __proto__문법을 썼다면 x5.__proto__, z4.__proto__ 이렇게 객체마다 프로토타입 설정을 해야 하는데 bmw 객체에 prototype으로 프로토타입을 설정해두고 필요한 곳에서 bmw 객체만 상속받아 사용하면 되니까.


instance of

생성자 함수를 이용하여 새로운 객체를 만들면 그 새로운 객체를 instance 라고 부른다.

instanceof 함수를 이용하면 생성자와 새로운 객체를 비교할 수 있다.

위의 코드로 예시를 들면 z4 instanceof Bmw 입력시 true가 나온다. z4가 Bmw 생성자로 만들어진 객체, 즉, Bmw의 instance이기 때문이다.

z4.constructor === Bmw; 를 입력해도 true가 나온다. z4의 생성자는 Bmw이기 때문이다.

다만 이런 instance의 값들은 임의로 변경이 가능한데 이런 임의 변경을 막는 방법은 클로저를 이용하는 것이다.


클로저를 사용한 코드

사용전 코드 (color 값을 객체 선언 후 임의로 변경 가능)

<script>
	
    const Bmw = function (color) {
    	this.color = color;
    };
    
    const x5 = new Bmw("red");
</script>

클로저 사용 코드 (선언 이후 임의로 값 변경 불가능)

<script>
	
    const Bmw = function (color) {
    	const c = color;
        this.getColor = function () => {
        	console.log(c)
        }
    }
    
    const x5 = new Bmw("red");
    
</script>

Bmw의 인스턴스 생성 시 생성자로 입력된 "red"를 Bmw 객체 안의 c 변수가 기억하고 있는 클로저를 이용.
this.getColor 안에서 c를 호출하면 초기의 값을 기억한 c 값이 호출된다. 생성자를 통한 변경이 아니면 이후에 직접 속성에 접근하여 수정한 들어가는 것은 불가능하다.

profile
하고 싶은 게 너무 많습니다.

0개의 댓글

Powered by GraphCDN, the GraphQL CDN