자바스크립트는 명령형, 함수형, 프로토타입 기반, 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어이다.
//이름과 주소 속성을 갖는 객체
const person={
name: 'lee',
address: 'seoul'
};
console.log(person); //{name: 'lee', address: 'seoul'}
상속은 객체지향 프로그래밍의 핵심 개념으로, 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.
안좋은 예제를 살펴보자
//생성자 함수
function Circle(radius) {
this.radius = radius;
this.getArea = function() {
//Math.PI는 원주율을 나타내는 상수이다.
return Math.PI * this.radius ** 2;
}
}
//반지름이 1인 인스턴스 생성
const circle1 = new Circle(1);
//반지름이 2인 인스턴스 생성
const circle2 = new Circle(2);
//Circle 생성자 함수는 인스턴스를 생성할 때마다 동일한 동작을하는 getArea메서드를 중복 생성하고 모든 인스턴스가 중복 소유한다. ---> 이것은 메모리 낭비 하나만 생성하여 모든 인스턴스가 공유해서 사용해야한다.
console.log(circle1.getArea === circle2.getArea); // false
좋은 예제
// getArea 함수를 Circle 생성자 함수의 프로토타입에 추가를 한다. 이렇게 하면 circle1과 circle2는 같은 프로토타입을 공유하게 되므로 getArea 함수를 동일한 곳에서 상속받게 된다.
//console.log(circle1.getArea === circle2.getArea);은 true를 반환.
두 객체가 동일한 함수를 참조하고 있기 때문이다.
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.getArea = function () {
return Math.PI * this.radius ** 2;
};
const circle1 = new Circle(1);
const circle2 = new Circle(2);
console.log(circle1.getArea === circle2.getArea); // true
그림의 빨간 박스로 표시된 것이 person 객체의 프로토타입인 object.prototype이다.
proto 접근자 프로퍼티를 통해 person 객체의 [[Prototype]] 내부 슬롯이 가리키는 객체인 object.prototype에 접근한 결과를 브라우저가 콘솔에 표시한 것이다.
모든 객체는 proto 접근자 프로퍼티를 통해 프로토타입을 가리키는 [[Prototype]] 내부 슬롯에 접근할 수 있다.
proto는 접근자 프로퍼티이다.
proto접근자 프로퍼티를 통해 프로토타입에 접근하는 이유
const parent = {};
const child = {};
//child의 프로토티입을 parent로 설정
child.__proto__ = parent;
//parent 프로토티입을 child 설정
parent.__proto__ = child; // TypeError: Cyclic __proto__ value
proto접근자 프로퍼티를 코드내에서 직접 사용하는 것은 권장하지 않는다.
// 프로토타입 체인의 종점이라서 Object.__proto__ 를 상속받을 수 없음.
const obj = Object.create(null);
// obj는 Object.__proto__ 를 상속받을 수 없음.
console.log(obj.__proto__); // undefined
//따라서 __proto__보다 Object.getPrototypeOf 메서드를 사용하는 편이 좋다.
console.log(Object.getPrototypeOf(obj)); // null
// 화살표 함수는 non-constructor 이다.
const Person = (name) => {
this.name = name;
};
//non-constructor는 prototype 프로퍼티를 소유하지 않는다.
console.log(Person.hasOwnProperty('prototype')); // false
//non-constructor는 프로토타입을 생성하지 않는다.
console.log(Person.prototype); // undefined
// es6의 메서드 축약표현으로 정의한 메서드는 non-constructor이다.
const obj = {
foo() {}
};
//non-constructor는 prototype 프로퍼티를 소유하지 않는다.
console.log(obj.foo.hasOwnProperty('prototype')); // false
//non-constructor는 프로토타입을 생성하지 않는다.
console.log(obj.foo.prototype); // undefined
생성자 함수로 객체를 생성한 후 proto접근자 프로퍼티와 prototype프로퍼티로 프로토타입 객체에 접근해보자.
function Person(name) {
this.name = name;
}
const me = new Person('chanmi');
// 동일한 프로토타입을 가리킨다.
console.log(Person.prototype === me.__proto__); // true
// constructor 가 평가되어 함수 객체 생성 시점에 프로토타입도 더불어 생성됨.
console.log(Person.prototype); // {constructor: f}
function Person(name) {
this.name = name;
}
(빌트인 생성자란 JavaScript에 내장된 기본 생성자 함수)
//obj가 객체 리터를을 통해 생성되었고 이 객체 리터럴은 내장된 object 생성자 함수에 의해 생성되었다.
const obj = { x: 1 };
console.log(obj.constructor === Object); // true
//obj가 x 라는 속성을 가지고 있음
console.log(obj.hasOwnProperty('x')); // true
//obj 객체는 object 생성자 함수에 의해 생성되었다. constructor 프로퍼티는 object를 가르킨다.
//obj 역시 x 라는 속성을 가지고 있음
const obj = new Object();
obj.x = 1;
console.log(obj.constructor === Object); // true
console.log(obj.hasOwnProperty('x')); // true
// Person 생성자 함수 정의
function Person(name) {
this.name = name;
}
//sayHello 메서드를 Person의 프로토타입에 추가
Person.prototype.sayHello = function() {
console.log(`hi my name is ${this.name}`);
}
//me 라는 객체를 Person 생성자 함수를 통해 만들고
const me = new Person('PPP');
//sayHello 메서드를 호출!
me.sayHello(); // hi my name is PPP