JS 문법 - 프로토타입

KODYwiththeK·2022년 12월 21일
0

JavaScript

목록 보기
25/32

JS 문법 - 프로토타입

Class: 제로베이스
Created: December 21, 2022 5:45 AM
Type: Javascript
강의 명: 초심자도 빈틈없이 학습하는 자바스크립트

프로토타입

프로토타입은 여러개 봤지만 처음봤을때 이거 보고 이해 바로했다.아래 명강의부터 먼저 요약해볼게요.

https://www.youtube.com/watch?v=wUgmzvExL_E

자바스크립트의 객체는 명세서에서 명명한 [[Prototype]]이라는 숨김 프로퍼티를 갖습니다. 이 숨김 프로퍼티 값은 null이거나 다른 객체에 대한 참조가 되는데, 다른 객체를 참조하는 경우 참조 대상을 '프로토타입(prototype)'이라 부릅니다.’

프로토타입 = 객체의 유전자

객체에는 자동으로 이렇게 프로토타입이라는 공간이 생긴다.

유전자라고 생각하면 편하다.

생성자함수 또는 객체. prototype. name = ‘kim’ 이렇게 프로토타입을 추가할 수 있다.

이렇게 부모요소(생성자함수)에 추가해 놓으면, 프로토타입이라는 유전자에 기록이 되고, 이 생성자함수를 사용하는 모든 인스턴스, 즉 자식요소들은 이 유전자를 가져다 쓸 수 있다.

예제 코드

function Animal(이름) {
	this.name = '이름';
  this.legs = 4;
	this.eat = true;
}

let rabbit = new Animal("흰 토끼");

Animal.prototype.live = true; // 부모요소에 프로토타입 추가
// rabbit.__proto__.live = true; => 이렇게 자식요소에 직접 추가할 수 있음

console.log(rabbit) // Animal { name: '이름', legs: 4, eat: true }
console.log(Animal.prototype) // { live: true }
console.log(rabbit.live) // true
  1. Animal 이라는 생성자함수(부모요소)에는 name, legs, eat 이라는 유전자가 있다.
  2. 생성자함수를 통해 rabbit이라는 자식 요소를 생성.
  3. rabbit이라는 자식 요소는 부모요소가 가진 name, legs, eat 요소 그대로 물려받음.
    rabbit 을 출력했을 떄 Animal이 가진 속성을 그대로 가진 것을 볼 수 있다.
  4. Animal의 prototype에 live 라는 유전자를 추가함
  5. rabbit에는 추가해준 적 없지만, 부모요소인 Animal에 있으니 rabbit에서도 활용 가능한 것을 확인할 수 있다. rabbit이 직접 가지고 있는 prototpye은 아니지만, 부모요소가 들고있으면 활용가능하다.

그렇다면 기본 메소드는 어떤 원리로…?

배열자료형 같은걸 다룰 때, 우리는 아무렇지도 않게 기본 메소드인 length( ), sort( ) 함수를 기본적으로 사용할 수 있다. 우리가 직접 배열에 sort 나 length 같은 함수 추가해준 적 없는데, 어떻게 가능한걸까?

let arr1 = [4,3,2]
let arr2 = new Array(5,4,3)
  • 배열을 만드는 방법은 다음과같이 두가지 방법이 있다. 두 가지 모두 배열을 만드는 같은 문법.
  • 하지만 arr1과 같은 방식은, 인간 입장에서 편리한 방법. 컴퓨터 입장에서는 new Array와 같은 방법으로 내부적으로 동작해서 배열을 만든다.

JS 기본 자료형에서의 new 키워드?

  • Array라는 부모요소 옆에 자식요소로 만들 것을 적으면(5,4,3) 빵틀로 빵을 만들듯 자식 요소가 생성되는 문법이다. 그걸 변수에 담아 사용하는거고.
  • Array라는 빵틀에 (5,4,3)이라는 자식요소를 만드는 것.
  • 마찬가지로 Array 뿐 아니라, 자바스크립트는 기본적으로 Object, String 등의 빵틀을 기본적으로 가지고 있다.

암튼 우리는 Array에 sort 함수 추가해준적 없는데 사용할 수 있는 이유는, sort와 같은 기본 함수들이 부모요소(Array)에 기록이 되어있기 때문인 것.

검증해보자.

let arr2 = new Array(5,4,3) 
console.log(Array.prototype)

// 출력 값
[constructor: ƒ, at: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ,]
at: ƒ at()
concat: ƒ concat()
constructor: ƒ Array()
copyWithin: ƒ copyWithin()
entries: ƒ entries()
.
.
.
includes: ƒ includes()
indexOf: ƒ indexOf()
join: ƒ join()
keys: ƒ keys()
lastIndexOf: ƒ lastIndexOf()
length: 0
// ...등등
[[Prototype]]: Object

Array. prototype을 출력해보면 다음과 같이 Array라는 부모요소가 기본적으로 가지고있는 여러가지 유전자, 즉 프로토타입을 볼 수 있다.

따라서 Array로 만들어진 Array의 자식요소들은 모두 위 함수를 사용할 수 있는 것이다.

내가만든 생성자함수 VS Array 생성자 함수

우리가 직접 만든 함수는 prototype에는 공통된 암시적으로 생성된 함수들이 없다.

Info라는 함수는 자바스크립트 내 기본적으로 들어있는 생성자함수가 아니기 때문에, 암시적으로 프로토타입이 만들어지지 않기 때문에, 명시적으로 직접 생성해줘야 함.

이렇게 공통적으로 사용되는 함수에 대해서는, 이런식으로 각각에 객체 내 메모리를 할당해서 메소드를 정의하는 것이 아닌, 밖으로 빼서 프로토타입으로 만들어서 메모리를 공유하도록 만드는 것이 메모리 효율에 좋다.

예제 코드

function Info(name, age) {
  this.name = name;
  this.age = age;
  this.func = function() {
    console.log(this.name, this.age)
  }
}
Info.prototype.write = function() {
  console.log(`이름은 ${this.name}, 나이는 ${this.age}입니다.`)
}

let info1 = new Info('Kody', 25)
let info2 = new Info('James', 30)
let info3 = new Info('Elena', 22)

console.log(info1) // Info { name: 'Kody', age: 25, func: [Function (anonymous)] }
console.log(info2) // Info { name: 'James', age: 30, func: [Function (anonymous)] }
console.log(info3) // Info { name: 'Elena', age: 22, func: [Function (anonymous)] }

  • 객체 하나하나마다 메모리를 할당하는 내부 함수를 사용하는 것 보다, 밖으로 빼서 프로퍼티를 만드는 것이 메모리 효율이 좋다!

내가만든함수

내가 만약에 Array를 사용하며, 특정한 기능을 구현하는 배열의 함수를 만들었다. 근데 그걸 너무 자주 사용하다보니, 어디 저장해두고 계속 가져다가 쓰려고한다. 그럼 내가 만든 함수를 모든 Array에서 가져다쓸 수 있도록 prototype으로 추가할 수도있을 것이다.

Array.prototype.내가만든함수 = function(){} // 

console.log(arr1.내가만든함수()) // undefined

위와같이 모든 Array에서 사용할 수 있도록 프로토타입에 내가만든함수를 추가할 수 있다.


함수의 디폴트 프로퍼티 prototype과 constructor

개발자가 특별히 할당하지 않더라도 모든 함수는 기본적으로 "prototype" 프로퍼티 갖는다.

디폴트 프로퍼티 "prototype"은 constructor 프로퍼티 하나만 있는 객체를 가리키는데, 여기서 constructor 프로퍼티는 함수 자신.

function Rabbit() {}
// 함수를 만들기만 해도 디폴트 프로퍼티인 prototype이 설정.
// Rabbit.prototype = { constructor: Rabbit }

console.log( Rabbit.prototype.constructor == Rabbit ); // true

constructor프로퍼티는 기존에 있던 객체의 constructor를 사용해 새로운 객체를 만들때 사용할 수 있다.

function Rabbit(name) {
  this.name = name;
  alert(name);
}

let rabbit = new Rabbit("흰 토끼");

let rabbit2 = new rabbit.constructor("검정 토끼");

이 방법은 객체가 있는데 이 객체를 만들 때 어떤 생성자가 사용되었는지 알 수 없는 경우(객체가 서드 파티 라이브러리에서 온 경우 등) 유용.

"constructor"를 이야기 할 때 가장 중요한 점은 자바스크립트는 알맞는  "constructor" 값을 보장하지 않는다는 점.

함수엔 기본으로 "prototype"이 설정된다라는 사실 그게 전부입니다. "constructor"와 관련해서 벌어지는 모든 일은 전적으로 개발자에게 달려있다.

함수에 기본으로 설정되는 "prototype" 프로퍼티 값을 다른 객체로 바꾸면, new를 이용해 객체를 만들었지만 객체에 “constructor”가 없는걸 볼 수 있다.

function Rabbit() {}
Rabbit.prototype = {
  jumps: true
};

let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false

이런 상황을 방지하고 constructor의 기본 성질을 제대로 활용하려면 "prototype"에 뭔가를 하고 싶을 때 "prototype" 전체를 덮어쓰지 말고 디폴트 "prototype"에 원하는 프로퍼티를 추가, 제거해야 함.

function Rabbit() {}

// Rabbit.prototype 전체를 덮어쓰지 말고
// 원하는 프로퍼티가 있으면 그냥 추가됨.
Rabbit.prototype.jumps = true
// 이렇게 하면 디폴트 프로퍼티 Rabbit.prototype.constructor가 유지.

같은 생성자로부터 생성된 객체들은 프로토타입을 공유한다.

프로토타입 체인

상위 프로토타입 속성 및 메소드를 공유하는 것을 말한다.

  • 자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근시 객체 자신의 것뿐 아니라 proto가 가리키는 링크를 따라서 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 접근할 수 있다.
  • 즉, 특정 객체의 프로퍼티나 메소드 접근시 만약 현재 객체의 해당 프로퍼티가 존재하지 않는다면 proto가 가리키는 링크를 따라 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례로 검색하는 것이 바로 프로토타입 체인이다.
  • 모든 프로토타입 체이닝의 종점은 Object.prototype이다.
  • 하위 객체는 상위 객체의 프로퍼티나 메소드를 상속받는 것이 아니라 공유한다.
  • 해당 객체에 없는 프로퍼티나 메소드를 접근할 때 프로토타입 체이닝이 일어난다.

예제 코드

function Info(name, age) {
  this.name = name;
  this.age = age;
}
Info.prototype.write = function() {
  console.log(`이름은 ${this.name}, 나이는 ${this.age}입니다.`)
}

let info1 = new Info('Kody', 25)

let parent1 = info1.__proto__;
console.log(parent1); // { write: [Function (anonymous)] }

let parent2 = parent1.__proto__;
console.log(parent2); // [Object: null prototype] {}

let parent3 = parent2.__proto__;
console.log(parent3); // null
  • 가장 최상위 객체, 즉 프로토타입 체인의 종점은 null인 것을 알 수 있다.
profile
일상 속 선한 영향력을 만드는 개발자를 꿈꿉니다🧑🏻‍💻

0개의 댓글

관련 채용 정보