Prototype언어 Javascript

BirdsOnTree·2022년 11월 27일
0

Javascript

목록 보기
17/17
post-thumbnail

자바스크립트에는 클래스라는 개념이 없다. 그래서 객체를 상속하기 위하여 프로토타입 이라는 방식을 사용한다. 또한 자바스크립트는 기존의 객체를 복사하여 새로운 객체를 생성하는 프로토타입 기반의 언어이다.

Prototype과 반환

만약에

function Person(name, age) {
	this.name = name,
    this.age = age,
      ...
}

const person1 = new Person('Bob', 30)    
    
person1.valueOf()

을 실행하면?

  1. 우선 Person 객체가 valueOf() 메소드를 가지고 있는지 체크한다.
  2. 없다면, Person의 프로토타입 객체(Person() 생성자의 프로토타입)에 valueOf() 메소드가 있는지 확인한다.
  3. 없다면, Person() 생성자의 프로토타입 객체의 프로토타입 객체(Object() 생성자의 프로토타입)가 valueOf() 메소드를 가지고 있는지 확인한다.

Prototype이란?

javascript에서 기본 데이터 타입을 제외한 모든것이 객체인데, 객체가 만들어지기 위해서는 자신을 만드는 데 사용된 원형인 프로토타입 객체를 이용해서 객체를 만든다. 이때 만들어진 객체 안에 __proto__ (비표준) 속성이 자신을 만들어낸 원형을 의미하는 프로토타입 객체를 참조하는 숨겨진 링크가 있다. 이 숨겨진 링크를 프로토타입이라고 정의한다.

프로토타입 기반 언어는 객체 원형인 포로토타입을 이용하여 새로운 객체를 만들어낸다. 이렇게 만든 객체 역시 또 다른 객체의 원형이 될 수 있다.

프로토타입은 크게 두가지로 해석되는데,

  1. 프로토타입 객체를 참조하는 ptorotype 속성
  2. 객체 멤버인 proto 속성이 참조하는 숨은 링크

이 둘의 차이점을 이해하기 위해서는 javascript 함수와 객체의 내부적인 구조를 이해해야 한다.

이 글에서는 javascript의 함수와 객체 내부 구조부터 시작하여 프로토타입에 대해 알아보려 한다.

함수와 객체의 내부 구조

javascript에서는 함수를 정의하고, 파싱*단계에 들어가면, 내부적으로 수행되는 작업이 있다.
함수 멤버로 prototype 속성이 있다. 이 속성은 다른곳에 생성된 함수이름의 프로토타입 객체를 참조한다.
프로토타입 객체의 멤버인 constructor 속성은 함수를 참조하는 내부구조를 가진다.

*파싱 : 어떤 큰 자료에서 원하는 정보만 가공하고 뽑아서 원할 때에 불러올 수 있게 하는 것, 컴퓨터의 JSON, XML, HTML 등으로 구성된 데이터를 분석하여 내가 원하는 부분만 추출하고자 하는 것

속성이 하나도 없는 Person이라는 함수가 정의되고, 파싱단계에 들어가면 Person 함수 Prototype 속성의 프로토타입 객체를 참조한다. 프로토타입 객체 멤버인 constructor 속성은 Person 함수를 참조하는 구조를 가진다.

Person 함수의 prototype 속성이 참조하는 프로토타입 객체는 new라는 연산자와 Person 함수를 통해 생성된 모든 객체의 원형이 되는 객체이다. 생성된 모든 객체가 참조한다는 것이다.

javascript에서는 기본 데이터 타입인 boolean, number, string, null, undefined 를 빼고는 모두 객체이다. 사용자가 정의한 함수 또한 객체이고, new 라는 연산자를 통해 생성된 것도 객체이다.

그리고 객체 안에는 proto(비표준) 속성이 있다. 이 속성은 객체가 만들어지기 위해 사용된 원형인 프로토타입 객체를 숨은 링크로 참조하는 역할을 한다.

프로토타입 객체란?

함수를 정의하면 다른 곳에 생성된 프로토타입 객체는 자신이 다른 객체의 원형이 되는 객체이다. 모든 객체는 프로토타입 객체에 접근할 수 있다. 프로토타입 객체도 동적으로 런타임에 멤버를 추가할 수 있다. 같은 원형을 복사로 생서된 모든 객체는 추가된 멤버를 사용할 수 있다.

function Person() {};

const Joon = new Person();
const Jisoo = new Person();

Person.prototype.getType = function() {
	return "인간";
}

console.log(Joon.getType()); //인간
console.log(Jisoo.getType()); //인간

소스코드의 6라인은 함수 안에 prototype 속성을 이용하여 멤버를 추가하였다. 프로토타입 객체에 getType()이라는 함수를 추가하면 멤버를 추가하기 전에 생성된 객체에서도 추가된 멤버를 사용할 수 있다.
같은 프로토타입을 이용하여 joon과 jisoo 객체는 getType()을 사용할수 있게 됐다.

여기서 알아두어야 할 것은 프로토타입 객체에 멤버를 추가, 수정, 삭제할 때는 함수 안의 prototype 속성을 사용해야 한다. 하지만 프로토타입 멤버를 읽을때는 함수 안의 prototype 속성 또는 객체의 이름으로 접근한다.

Joon.getType = function() {
	return '사람';
}

console.log(Joon.getType()); //사람
console.log(Jisoo.getType()); //인간

Jisoo.age = 25;

console.log(Joon.age); // undefined
console.log(Jisoo.age); // 25

인간은 prototype에 추가한 getType(), 사람은 Joon에 추가한 getType()

1라인에는 Joon 객체를 이용하여 getType() 리턴 값을 사람으로 수정하였다. 그리고 Joon과 Jisoo에서 각각 getType()을 호출하면 Joon 객체를 이용하여 호출한 결과는 사람으로 출력되고, Jisoo로 호출한 결과는 인간으로 출력된다.

javaScript에서는 객체 인스턴스와 프로토타입 간에 연결(많은 브라우저들이 생성자의 prototype 속성에서 파생된 __proto__ 속성으로 객체 인스턴스에 구현하고 있습니다.)이 구성되며 이 연결을 따라 프로토타입 체인을 타고 올라가며 속성과 메소드를 탐색하게 된다.

Person.prototype.getType = function() {
	return "사람";
}

console.log(Jisoo.getType()); //사람

함수의 prototype 속성을 이용하여 getType() 리턴값을 사람으로 수정하였다. 그리고 jisoo 객체를 이용하여 호출한 결과 사람이 나왔다.

프로토타입 객체는 새로운 객체가 생성되기 위한 원형이 되는 객체이고, 같은 원형으로 생성된 객체가 공통으로 참조하는 공간이다.

코드의 재사용

코드의 재사용 하면 떠오르는 단어는 상속이다. 클래스라는 개념이 있는 Java에서는 중복된 코드를 상속받아 코드를 재활용을 할 수 있다. 하지만 javascript에서는 클래스가 없는 프로토타입 기반의 언어이다. 그래서 프로토타입을 이용하여 코드를 재사용할 수 없다.

상속하는 방법에도 크게 두가지로 분류할 수 있다. classical 방식과 prototypal 방식이다. classical 방식은 new 연산자를 통해 생성한 객체를 사용하여 코드를 재사용 하는 방법이다. prototypal 방식은 리터럴 또는 Object.create()를 이용하여 객체를 생성하고 확장해 가는 방식이다. javacript에서는 후자를 더 선호한다. 그 이유는 더 간결하게 구현할수 있기 때문이다.

프로토타입 공유

부모자를 한번도 호출하지 않으면서 프로토타입 객체를 공유하는 방법이다.

function Person(name) {
	this.name = name || "혁준";
}

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

function Korean(name) {
	this.name = name;
}

Korean.prototype = Person.prototype;

const kor1 = new Korean('지수');
console.log(kor1.getName()); //지수

12라인에서 자식 함수의 prototype 속성을 부모함수의 prototype 속성이 참조하는 객체로 설정했다.

Korean.prototype = Person.prototype;

자식 함수를 통해 생성된 객체는 부모 함수를 통해 생성된 객체를 거치지 않고 부모 함수의 프로토타입 객체를 부모로 지정하여 객체를 생성한다. 부모 함수의 내용을 상속받지 못하므로 상속받으려는 부분을 부모 함수의 프로토타입 객체에 작성해야 사용자가 원하는 결과를 얻게 된다.

부모 프로토타입 객체로 링크가 참조됐다.

prototypal한 방식의 재사용 (Object.create())

Object.create()를 사용하여 객체를 생성과 동시에 프로토타입객체를 지정한다.
이 함수는 첫 번째 매개변수는 부모객체로 사용할 객체를 넘겨주고, 두 번째 매개변수는 선택적 매개변수로써 반환되는 자식객체의 속성에 추가되는 부분이다.

const person = {
	type: '인간',
  	getType: function() {
    	return this.type;
    },
  	getName: function() {
    	return this.name;
    },
};

const joon = Object.create(person);
joon.name = '혁준';

console.log(joon.getType()); // 인간
console.log(joon.getName()); // 혁준

1라인에서 부모 객체에 해당하는 person을 객체 리터럴 방식으로 생성했다. 그리고 11라인에서 자식 객체 joon은 Object.create() 함수를 이용하여 첫 번째 매개변수로 person을 넘겨받아 joon 객체를 생성했다.

출처:
https://www.nextree.co.kr/p7323/
https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/Object_prototypes

0개의 댓글