- 속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조를 객체 라고 한다.
- 자바스크립트는 객체 기반의 프로그래밍 언어이며, 원시타입을 제외하고 자바스크립트를 이루고 있는 거의 모든것이 객체다.
- 실세계의 실체를 인식하는 철학적 사고를 프로그래밍에 접목하려는 시도에서 시작한다.
- 실체는 특징이나 성질을 나타내는 속성을 가지고 있고, 이를 통해 실체를 인식하거나 구별할 수 있다.
ex) 사람은 이름, 주소, 나이, 성별, 신장, 체중, 학력 등의 다양한 속성을 갖는다.
const person = {
name: 'Lee',
address: 'Seoul'
};
- 프로그램을 명령어 또는 함수의 목록으로 보는 전통적인 명령형 프로그래밍의 절차지향적 관점에서 벗어나 여러개의 독립적 단위, 즉 객체 집합으로 프로그램을 표현하려는 프로그래밍 패러다임.
- 객체지향 프로그래밍은 객체의 상태를 나타내는 데이터와 상태 데이터를 조작할 수 있는 동작을 하나의 논리적인 단위로 묶어 생각한다. 따라서 객체는 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조라고 할 수 있다.
- 이 때, 객체의 상태 데이터를 프로퍼티, 동작을 메서드 라 부른다.
- 간혹 C++ 이나 자바 같은 클래스 기반 객체지향 프로그래밍 언어의 특징인 클래스와 상속, 캡슐화를 위한 키워드인 public, private, protected 등이 없어서 자바스크립트는 객체지향 언어가 아니라고 오해하는 경우도 있다.
- 자바스크립트는 프로토타입 기반의 객체지향 프로그래밍 언어다.
- 클래스가 도입이 되긴 했지만, 기존의 프로토타입 기반 객체지향 모델을 폐지하고 새로운 객체지향 모델을 제공하는 것은 아니다.
- 사실 클래스도 함수이며, 기존 프로토타입 기반 패턴의 syntactic sugar 라고 볼 수 있다.
- 클래스와 생성자 함수 모두 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지는 않는다. 클래스는 생성자 함수보다 엄격하며 클래스는 생성자 함수에서는 제공하지 않는 기능도 제공한다.
- 상속은 객체지향 프로그래밍의 핵심 개념으로, 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.
- 자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거한다. 중복을 제거하는 방법은 기존의 코드를 적극적으로 재사용 하는 것이다.
// 프로토타입 상속 x
function Circle1(radius) {
this.radius = radius;
this.getArea = function () {
return Math.PI * this.radius ** 2;
};
}
const circle1 = new Circle1(1);
const circle2 = new Circle1(2);
console.log(circle1.getArea === circle2.getArea); // false
// 프로토타입 상속 o
function Circle2(radius) {
this.radius = radius;
}
Circle2.prototype.getArea = function () {
return Math.PI * this.radius ** 2;
};
const circle3 = new Circle2(3);
const circle4 = new Circle2(4);
console.log(circle3.getArea === circle4.getArea); // true
- 자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있다. 그리고 이것은 마치 객체 지향의 상속 개념과 같이 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있게 한다.
- 이러한 부모 객체를 Prototype(프로토타입) 객체 또는 줄여서 Prototype(프로토타입)이라 한다.
- Prototype 객체는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용한다.
- 모든 객체는 하나의 프로토타입을 갖고, 모든 프로토타입은 생성자 함수와 연결되어 있다.
- 모든 객체는
[[Prototype]]
이라는 내부 슬롯을 가지며, 이 내부 슬롯의 값은 프로토타입의 참조 ( null 인 경우도 있다 ) 다.- 객체가 생성될 때 객체 생성 방식에 따라 프로토타입이 결정되고
[[Prototype]]
에 저장된다. 결정된 프로토타입 객체는 다른 임의의 객체로 변경할 수 있다.- 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다. 이러한 특징을 활용하여 객체의 상속을 구현할 수 있다.
- [[Prototype]]의 값은 Prototype(프로토타입) 객체이며 __proto__ accessor property 로 접근할 수 있다. __proto__ 프로퍼티에 접근하면 내부적으로 Object.getPrototypeOf가 호출되어 프로토타입 객체를 반환한다.
let student = {
name: 'Lee',
score: 90
};
// student 객체는 __proto__ 프로퍼티로
// 자신의 부모 객체(프로토타입 객체)인 Object.prototype을 가리키고 있다.
// student에는 hasOwnProperty 메소드가 없지만 아래 구문은 동작한다.
console.log(student.hasOwnProperty('name')); // true
- 모든 객체는 자신의 프로토타입 객체를 가리키는 [[Prototype]] 인터널 슬롯(internal slot) 을 갖으며 상속을 위해 사용된다.
- 함수도 객체이므로 [[Prototype]] 인터널 슬롯을 갖는다.
그런데 함수 객체는 일반 객체와는 달리 prototype 프로퍼티도 소유하게 된다.
- 프로토타입 객체는 constructor 프로퍼티를 갖는다.
- 이 constructor 프로퍼티는 객체의 입장에서 자신을 생성한 객체를 가리킨다.
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
// Person() 생성자 함수에 의해 생성된 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(Person.prototype.constructor === Person);
// foo 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(foo.constructor === Person);
// Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다.
console.log(Person.constructor === Function);
- 자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면 [[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색한다. 이것을 프로토타입 체인이라 한다.
let student = {
name: 'Lee',
score: 90
};
// student 객체는 __proto__ 프로퍼티로
// 자신의 부모 객체(프로토타입 객체)인 Object.prototype을 가리키고 있다.
// student에는 hasOwnProperty 메소드가 없지만
// 프로토타입 체인에 의해 아래 구문은 동작한다.
console.log(student.hasOwnProperty('name')); // true
- 객체 리터럴 방식으로 생성된 객체는 결국 내장 함수(Built-in)인 Object() 생성자 함수로 객체를 생성하는 것을 단순화시킨 것이다.
- 자바스크립트 엔진은 객체 리터럴로 객체를 생성하는 코드를 만나면 내부적으로 Object() 생성자 함수를 사용하여 객체를 생성한다.
- Object() 생성자 함수는 물론 함수이다. 따라서 함수 객체인 Object() 생성자 함수는 일반 객체와 달리 prototype 프로퍼티가 있다.
let person = {
name: 'Lee',
gender: 'male',
sayHello: function(){
console.log('Hi! my name is ' + this.name);
}
};
console.log(person.__proto__ === Object.prototype); // ① true
console.log(Object.prototype.constructor === Object); // ② true
console.log(Object.__proto__ === Function.prototype); // ③ true
console.log(Function.prototype.__proto__ === Object.prototype); // ④ true
- 결론적으로 객체 리터럴을 사용하여 객체를 생성한 경우, 그 객체의 프로토타입 객체는
Object.prototype
이다.
- 함수선언식, 함수표현식 모두 함수 리터럴 방식을 사용한다. 함수 리터럴 방식은 Function() 생성자 함수로 함수를 생성하는 것을 단순화 시킨 것이다.
- 3가지 함수 정의 방식은 결국 Function() 생성자 함수를 통해 함수 객체를 생성한다, 따라서 어떠한 방식으로 함수 객체를 생성하여도 모든 함수 객체의 prototype 객체는 Function.prototype이다.
- 생성자 함수도 함수 객체이므로 생성자 함수의 prototype 객체는 Function.prototype이다.
- 객체 리터럴 방식이나 생성자 함수 방식이나 결국은 모든 객체의 부모 객체인 Object.prototype 객체에서 프로토타입 체인이 끝난다. 이때 Object.prototype 객체를 프로토타입 체인의 종점(End of prototype chain) 이라 한다.
function Person(name, gender) {
this.name = name;
this.gender = gender;
this.sayHello = function(){
console.log('Hi! my name is ' + this.name);
};
}
var foo = new Person('Lee', 'male');
console.log(foo.__proto__ === Person.prototype); // ① true
console.log(Person.prototype.__proto__ === Object.prototype); // ② true
console.log(Person.prototype.constructor === Person); // ③ true
console.log(Person.__proto__ === Function.prototype); // ④ true
console.log(Function.prototype.__proto__ === Object.prototype); // ⑤ true
- 프로토타입 객체도 객체이므로 일반 객체와 같이 프로퍼티를 추가/삭제할 수 있다. 그리고 이렇게 추가/삭제된 프로퍼티는 즉시 프로토타입 체인에 반영된다.
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
Person.prototype.sayHello = function(){
console.log('Hi! my name is ' + this.name);
};
foo.sayHello();
- 자바스크립트에서 원시 타입(숫자, 문자열, boolean, null, undefined)을 제외한 모든것은 객체이다. 그런데 아래 예제를 살펴보면 원시 타입인 문자열이 객체와 유사하게 동작한다.
- 원시 타입으로 프로퍼티나 메소드를 호출할 때 원시 타입과 연관된 객체로 일시적으로 변환되어 프로토타입 객체를 공유하게 된다. (Wrapper Object )
- 객체를 생성할 때 프로토타입은 결정된다. 결정된 프로토타입 객체는 다른 임의의 객체로 변경할 수 있다.
- 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다.
- 이러한 특징을 활용하여 객체의 상속을 구현할 수 있다.
- 객체의 프로퍼티를 참조하는 경우, 해당 객체에 프로퍼티가 없는 경우, 프로토타입 체인이 동작한다.
- 객체의 프로퍼티에 값을 할당하는 경우, 프로토타입 체인이 동작하지 않는다.
- 이는 객체에 해당 프로퍼티가 있는 경우, 값을 재할당하고 해당 프로퍼티가 없는 경우는 해당 객체에 프로퍼티를 동적으로 추가하기 때문이다.
- poiemaweb - prototype
- 자바스크립트 딥 다이브 260p ~ 311p