LECTURE/JavaScript
프로토타입 - 상속, 생성자 함수 프로토타입, Object 프로토타입, 모던 메소드
heywoo
2023. 2. 9. 00:16
프로토타입 상속
자바스크립트의 객체는 [[Prototype]]이라는 숨김 프로퍼티를 갖는다.
이 프로퍼티 값은 null이거나 다른 객체에 대한 참조가 되는데, 다른 객체를 참조하는 경우 참조 대상을 프로토타입(prototype)이라 부른다.
const user = {
activate : true,
login : function () {
console.log('로그인 되었습니다')
}
};
const student = {
passion : true
};
.__proto__ [[Prototype]] 의 getter, setter라 할 수 있다.
student.__proto__ = user;
console.log(student.activate); //true
student.login(); //로그인 되었습니다.
console.log(student.passion); //true
=> "student의 프로토타입은 user이다." 또는 "student는 user를 상속 받는다." 라고 표현한다.
=> 프로토타입에서 상속 받은 프로퍼티를 상속 프로퍼티라고 한다.
프로토타입 체인
object에서 프로퍼티를 읽으려 할 때 해당 프로퍼티가 없으면 자바스크립트는 자동으로 프로토타입에서 프로퍼티를 찾는다.
const greedyStudent = {
class : 11,
__proto__ : student
};
console.log(greedyStudent.activate); //user에서 상속 받은 상속 프로퍼티
console.log(greedyStudent.passion); //student에서 상속 받은 상속 프로퍼티
프로토타입 특징
const user = {
id : 'user',
login () {
console.log(`${this.id}님 로그인 되었습니다.`)
}
};
const student = {
__proto__ : user
};
- 프로퍼티 추가, 수정 삭제
프로토타입은 프로퍼티를 읽을 때만 사용하며 프로퍼티 추가, 수정, 삭제 연산은 객체에 직접한다.
login 메소드 내의 this는 프로토타입에 영향 받지 않으며 this는 언제나 동작할 때 .앞에 있는 객체를 의미한다. (student)
student.id = 'user01';
student.login(); //user01님 로그인 되었습니다.
- for in 반복문
for in 반복문은 상속 프로퍼티도 순회 대상에 포함시킨다.
hasOwnProperty(key) : key에 대응하는 프로퍼티가 상속 프로퍼티가 아니라 객체에 직접 구현된 프로퍼티일 경우 true 반환
for(let key in student) {
console.log(key);
let isOwn = student.hasOwnProperty(key);
if(isOwn) {
console.log(`객체 자신의 property : ${key}`);
}
}
// 출력
id
객체 자신의 property : id
login
생성자 함수 프로퍼티
new 연산자를 사용해 만든 객체는 생성자 함수의 프로토타입 정보를 사용해서 [[Prototype]]을 설정한다.
const user = {
activate : true,
login () {
console.log('로그인 되었습니다');
}
};
function Student(name) {
this.name = name;
}
F.prototype은 new F를 호출할 때만 사용된다.
new F를 호출할 때 만들어지는 새로운 객체의 [[Prototype]]을 할당한다. (DNA가 전달된다고 생각해보자)
Student.prototype = user; // new Student를 호출할 때 만들어지는 새로운 객체마다 user를 상속하게 한다.
let student = new Student('홍길동'); //student.__proto__ == user (student의 조상은 user이다)
let student2 = new Student('유관순');
console.log(student.activate); // true
student2.login(); // 로그인 되었습니다
Student의 다양한 DNA 정보에 user를 추가한다고 생각한다면 new Student로 만들어진 객체에 user의 DNA 정보가 상속된다.
prototype 은 .__proto__ 와 달리 name, age와 같은 일반 프로퍼티인데, 다만 위와 같은 기능을 갖고 있는 것이다.
Object prototype
Object는 내장 객체 생성자 함수인데 이 생성자 함수의 prototype은 toString을 비롯한 다양한 메소드가 구현 된 거대한 객체를 참조한다.
new Object()나 {}를 사용해 객체를 만들 때 만들어진 객체의 [[Prototype]]은 Object.prototype을 참조한다.
(Object의 DNA 정보 즉, 다양한 메소드를 참조한다.)
const obj = {}; // new Object(); 와 같다
console.log(obj.__proto__ === Object.prototype); //true obj도 객체로서 Object의 메소드들을 쓸 수 있다.
console.log(obj.toString === obj.__proto__.toString); //true 상속 받은 toString이다.
console.log(obj.toString === Object.prototype.toString); //true DAN정보에 있는 toString이다
built in object prototype
Fucntion, String, Number를 비롯한 내장 객체들 역시 프로토타입에 메서드를 저장한다.
모든 내장 프로토타입 상속 트리 꼭대기에는 Object.prototype이 있어야 한다고 규정한다.
const num = new Number(100);
// num은 Number.prototype을 상속 받았는가?
console.log(num.__proto__ == Number.prototype); //ture
// num은 Object.prototype을 상속 받았는가?
console.log(num.__proto__.__proto__ === Object.prototype); //true
// 체인 맨 위에는 null이 있다.
console.log(num.__proto__.__proto__.__proto__); //null
Object.prototype에도 toString이 있지만 중복 메서드가 있는 경우 체인에서 가까운 메서드가 사용되어 Number.prototype의 toString이 사용된다.
console.log(num); //[Number: 100]
console.log(num.toString()); //100 ->toString 은 value값만 보여준다.
내장 프로토타입 수정 가능하다.
하지만 되도록 변경하지 않아야 하며, 명세서에 새로 등록된 기능을 쓰고 싶지만 자바스크립트 엔진에 구현되지 않은 경우 정도에만 변경한다.
String.prototype.hello = function () {
console.log(`hello, ${this}`);
}
"JavaScript".hello(); //hello, JavaScript
프로토 타입 접근 시 사용하는 모던 메소드
- Object.create(proto) : [[Prototype]] 가 proto를 참조하는 빈 객체를 만듦
const user = {
activate : true
};
const student = Object.create(user);
console.log(student.activate); //true
- Object.getPrototypeOf(obj) - obj의 [[Prototype]]을 반환
console.log(Object.getPrototypeOf(student)); //{ activate: true }
console.log(Object.getPrototypeOf(student) === user); //true
- Object.setPrototypeOf(obj, proto) - obj의 [[Prototype]]을 proto가 되도록 설정
Object.setPrototypeOf(student, {});
console.log(Object.getPrototypeOf(student)); // {}
console.log(Object.getPrototypeOf(student) === user); // false
__proto__를 getter, setter로 직접 사용하면 키가 "__proto__" 일 때 에러가 발생하는 의도치 않은 상황이 발생할 수 있다.
const obj = Object.create(user);
let key = "__proto__";
console.log(obj[key]); // = obj.__proto__ 와 같기에 { activate: true } 출력
obj[key] = { test : "새로운 객체 덮어쓰기"}
console.log(obj[key]); //{ test: '새로운 객체 덮어쓰기' }
console.log(obj.__proto__); //{ test: '새로운 객체 덮어쓰기' }