본문 바로가기

Web hacking

JavaScript 객체 및 prototype ( 2 )

참조 : https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/Object_prototypes

 

JavaScript는 흔히 프로토타입 기반 언어라 불린다. 모든 객체들이 메소드와 속성들을 상속받기 위한 템플릿으로써 프로토 타입 객체 (prototype object)를 가진다는 의미이다. 프로토타입 객체도 또 다시 상위 프로토 타입 객체로부터 메소드와 속성을 상속받을 수도 있고 그 상위 프로토타입 객체도 마찬가지다. 이를 프로토타입 체인(prototype chain) 이라 부르며 다른 객체에 정의된 메소드와 속성을 한 객체에서 사용할 수 있도록 하는 근간이다. 

 

정확히 말하자면 상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototype이라는 속성에 정의되어 있다. 

 

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

 

 

아래는 프로토타입 객체를 이해하기 위한 예시이다.

 

-생성자 함수-
function Person(first, last, age, gender, interests) {
  // 속성과 메소드 정의
  this.first = first;
  this.last = last;
  //...
}

-인스턴스-
var person1 = new Person("Bob", "Smith", 32, "male", ["music", "skiing"]);

 

console창에서 아래 그림과 같이 person1. 을 치면, 아래와 같은 결과를 보여준다. 

 

prototype 객체 이해

 

위에서 person1의 프로토타입 객체인 Person()에 정의된 멤버들 ( name, age, bio...)을 볼 수 있다. 또한 valueOf처럼 Person()의 프로토타입 객체인 Object에 정의된 다른 멤버들도 볼 수 있다. 이는 Prototype 체인이 동작한다는 증거이다. 

 

person1.valueOf();

 

Object에 정의되어 있는 메소드를 person1에서 호출해 보면, 호출된 객체의 값을 단순 반환하며, 일어나는 순서도는 아래와 같다. 

 

1. 브라우저는 우선 person1 객체가 valueOf() 메소드를 가지고 있는지 체크한다. 

2. 없으므로 person1의 프로토타입 객체( Person() 생성자의 프로토타입 )에 valueOf() 메소드가 있는지 체크한다. 

3. 여전히 없으므로 Person() 생성자의 프로토타입 객체의 프로토타입 객체 (Object() 생성자의 프로토타입)가 valueOf() 메소드를 가지고 있는지 체크한다. 여기에 있으니 호출하며 끝낸다. 

 

( 주의해야 할점은 프로토타입 체인에서 한 객체의 메소드와 속성들이 다른 객체로 복사되는 것이 아니며, 체인을 타고 올라가며 접근할 뿐이다. )

 

( 또 한가지, 많은 수의 모던 브라우저들이 __proto__ 속성을 통해 특정 객체의 프로토타입 객체에 접근할 수 있도록 구현하였다. 예를 들어, person1.__proto__ , person1.__proto__.__proto__  )

 

 

프로토 타입 속성: 상속받은 멤버들이 정의된 곳 

 

상속받는 멤버들은 prototype 속성 ( sub-namespace )에 정의되어 있다. Object.로 시작하는 게 아닌, Object.prototype.로 시작하는 것을 말한다. prototype 속성도 하나의 객체이며 프로토타입 체인을 통해 상속하고자 하는 속성과 메소드를 담아두는 버킷으로 주로 사용되는 객체이다. 

 

그래서 Object.prototype.valueOf(), Object.prototype.toString() 등등은 생성자를 통해 새로 생성되는 인스턴스는 물론 Object.prototype을 상속 받는 객체라면 어떤 객체에서든지 접근할 수 있다. 

( Object.is(), Object.keys() 등 prototype 버킷에 정의되지 않은 멤버들은 상속되지 않는다. 이 것들은 Object() 생성자에서만 사용할 수 있는 멤버들이다. )

 

전역 객체인 String, Date, Number,Array 의 프로토타입에 정의된 메소드와 속성들을 체크해 보면, 해당 프로토타입 객체들에는 이미 많은 수의 메소드가 정의되어 있는 것을 볼 수 있다. 

 

String 객체의 프로토타입에 정의된 메소드들..

 

그래서 아래와 같은 문자열 객체를 생성했을 떄, 

 

var myString = "This is my string.";

myString.split(" ")

 

myString 인스턴스가 생성되는 즉시 split(), indexOf() 등의 문자열을 위한 유용한 메소드들을 사용할 수 있다.

 

 

create() 메소드를 통해 프로토타입 객체 확인

 

var perons2 = Object.create(person1)

person2.__proto__;

 

create() 메소드가 실제로 하는 일은 주어진 객체를 프로토타입 객체로 삼아 새로운 객체를 생성한다. 즉 person2는 person1을 프로토타입 객체로 삼게 된다. 

 

 

생성자 속성

 

모든 생성자 함수는 constructor 속성을 지닌 객체를 프로토타입 객체로 가지고 있다. 이 constructor 속성은 원본 생성자 함수 자신을 가리키고 있다. Person.prototype 속성에 정의된 속성들은 Person() 생성자로 생성된 모든 인스턴스에서 사용할 수 있다. 그러므로 person1과 person2에서도 constructor 속성에 접근할 수 있다. 

 

person1.constructor;
person2.constructor;

 

두 구문 모두 Person() 생성자 함수를 반환한다. constructor 속성에 괄호를 붙이고 실행하여, 새 인스턴스를 생성하는 방법도 존재한다. 생성자도 함수의 일종이므로 괄호를 붙이면 실행할 수 있다. new 키워드를 통해 실행하면 함수를 인스턴스를 생성하기 위한 생성자로 사용할 수 있다. 

 

var person3 = new person1.constructor("Karen", "Stephenson", 26, "female", [
  "playing drums",
  "mountain climbing",
]);

 

위와 같은 방식을 자주 사용할 필요는 없지만 실행 도중 명시적인 생성자 함수를 예측할 수 없는 상황에서 인스턴스를 생성해야 하거나 하는 경우 유용하게 사용할 수 있는 방법이다. 

 

 

.....

 

 

 

 

 

 

 

참고

 

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object