객체
const object = {
test: 상태를 나타내는 데이터 (프로퍼티),
getTest(){ //메소드
동작
},
};
결론: 객체는 프로퍼티와 메소드를 하나의 논리적인 단위로 묶은 복합적인 자료구조 이며, 객체 지향 프로그래밍은 객체로 프로그램을 표현하려는 패러다임 이다.
자바스크립트는 프로토 타입을 기반으로 상속을 구현
function Test(x) {
this.x = x;
}
Test.prototype.getAnswer = function () {
return
}
//Test 생성자 함수가 생성한 모든 인스턴스가 getAnswer 메서드를 공유해서 사용하도록
//프로토타입에 추가
// 인스턴스 생성
const test1 = new Test(1)
const test2 = new Test(2)
console.dir( test1) //prototype 참조 확인 가능
console.dir( test2) //prototype 참조 확인 가능
prototype 객체 (= 프로토 타입 , === 부모 객체)test1.getAnswer모든 객체는 [[prototype]] 이라는 내부슬롯, prototype의 참조 을 가진다.
prototype 과 [[prototype]] 값의 결정prototype이 결정 → 내부 슬롯의 값 [[prototype]] 은 prototype의 참조이므로 객체 생성 방식에 따라 값도 저장__proto__ 접근자 프로퍼티console.dir( test1.__proto__)
그러나 __proto__ 를 사용할 수 없는 경우가 더러 있기에
Object.getPrototypeOf 메소드(참조획득),
Object.setPrototypeOf 메소드(프로토타입 교체) 사용을 권장
console.dir(Object.getPrototypeOf(test1));
Object.setPrototypeOf(test2, { newMethod: () => console.log("Hello") });
console.dir(test2);
//함수 객체는 prototype 프로퍼티를 소유한다. -> 생성자함수로 사용할 수 있게 하기 위해
(function () {}).hasOwnProperty('prototype') //true
//일반 객체는 prototype 프로퍼티를 소유하지 않는다
({}).hasOwnProperty('prototype') //false
결론 : 따라서 생성자 함수로서 호출 할 수 없는 함수, 즉 non-constructor 인 화살표 함수, 함수형 컴포넌트 등은 prototype 프로퍼티를 소유하지 않는다.
리터럴 표기법에 의해 생성된 객체도 프로토 타입이 존재 (생성자 함수로 인식)
그러나 해당 객체가 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 생성자 함수라고 단정할수 없다.
//객체 리터럴
const obj = {}
//함수 리터럴
const add = function(a,b) {return}
//배열 리터럴
const arr = [1,2,3]
//정규 표현식 리터럴
const test = /is/ig
console.log(obj.constructor); //Object.prototype
console.log(add.constructor); //Function.prototype
console.log(arr.constructor); //Array.prototype
console.log(test.constructor); //RegExp.prototype
객체 리터럴 문법을 사용해 엔진이 자동으로 프로토 타입 체인을 따라서 찾은 것이므로, Object 생성자가 직접 호출된 것은 아님.
프로토 타입과 생성자 함수는 단독으로 존재할 수 없기에 가상적인 생성자 함수를 가지면 자동으로 프로토타입도 연결된다.
그러나 리터럴 표기법으로 생성한 객체도 생성자 함수로 생성한 객체와 본질적인 면에서 큰 차이는 없기에, 객체.constructor 를 생성자 함수로 생각해도 무리는 없다.
프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성됨.
// 인스턴스 생성
const test1 = new Test(1)
const test2 = new Test(2)
console.dir( test1) //prototype 참조 확인 가능
console.dir( test2) //prototype 참조 확인 가능
function Test(x) {
this.x = x;
}
Test.prototype.getAnswer = function () {
return
}
결론: 런 타임 이전에 프로토 타입이 생성!
일반 함수와 마찬가지로 빌트인 생성자 함수가 생성되는 시점에 프로토타입이 생성된다. 모든 빌트인 생성자 함수는 전역 객체가 생성되는 시점(자바스크립트 코드실행시 가장 먼저 생성 ) 에 생성, 생성된 프로토타입은 빌트인 생성자 함수의 prototype 프로퍼티에 바인딩 된다.
결론: 전역객체가 생성되는 시점에 프로토타입이 생성!
자바스크립트는 객체의 프로퍼티에 접근하려고 할때 해당 객체에 접근하려는 프로퍼티가 없다면 [[prototype]] 내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로토타입의 프로퍼티를 순차적으로 검색한다. 이를 프로토타입 체인이라고 한다 프로토타입 체인은 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 매커니즘이다.
결론: 프로토타입 체인의 최상위에 위치하는 객체는 언제나 Object.prototype 이다 따라서 모든 객체는 Object.prototype을 상속받는다. Object.prototype을 프로토타입 체인의 종점이라한다.
console.dir( test1.__proto__.__proto__.__proto__) //null
이에 반해 프로퍼티가 아닌 식별자 는 스코프 체인에서 검색한다 따라서 스코프 체인은 식별자 검색을 위한 매커니즘 이라고 할 수 있다.
결론 : 스코프 체인과 프로토타입 체인은 서로 연관없이 별도로 동작하는 것이 아니라 서로 협력하여 식별자와 프로퍼티를 검색하는데 사용된다.
프로토타입이 소유한 프로퍼티를 프로토타입 프로퍼티, 인스턴스가 소유한 프로퍼티를 인스턴스 프로퍼티라고 부른다.
프로퍼티 섀이딩 : 부모 프로토타입의 프로퍼티랑 같은 이름의 프로퍼티를 인스턴스에 추가했을때, 프로토타입 체인을 따라 올라가지 않고 자식 객체에서 찾음 프로토타입 프로퍼티가 가려진다.
또한 하위객체를 통해 프로토 타입의 프로퍼티를 변경 또는 삭제하는것은 불가능 하다.
Test.prototype.sayHello = function () {
console.log('hi')
}
test1.sayHello() // hi
delete Test.prototype.sayHello
test1.sayHello() //error
결론: 상속관계에 의해 오버라이딩 & 프로퍼티 섀이딩 되므로, 프로토타입 프로퍼티를 변경 or 삭제시 하위객체에서 프로토 타입 체인으로 접근이 아니라 직접 접근해야한다.
프로토타입은 생성자 함수 또는 인스턴스에 의해 교체할 수 있다.
프로토 타입을 교체하면 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴된다.
const Test = (function () {
function Apple(x) {
}
Apple.prototype = {
Hi() {
console.log('hi');
}
};
return Apple;
}());
// 인스턴스 생성
const test1 = new Test();
console.dir(test1);
const Test = (function () {
function Apple(x) {
}
Apple.prototype = {
Hi() {
console.log('hi');
},
constructor: Apple //constructor 프로퍼티를 되살린다.
};
return Apple;
}());
// 인스턴스 생성
const test1 = new Test();
console.dir(test1);
결론: 생성자 함수로 인해 프로토 타입 교체시 constructor가 파괴되므로 프로퍼티에 추가 하여 되살리자!
function Test(x) {
this.x = x;
}
// 인스턴스 생성
const test1 = new Test(1)
const parent = {
sayHi(){
console.log('hi')
}}
Object.setPrototypeOf(test1,parent)
console.dir(test1)
인스턴스~~ 와 생성자 함수~~ 의 차이
//Test 생성자 함수 자체에 prototype이 변경 된게 아님
const test2 = new Test(1)
const test3 = new Test(1)
test1.sayHi();
test2.sayHi();
test3.sayHi();
결론 : 프로토타입은 직접 교체하지 않는 것이 좋다. 상속 관계를 인위적으로 설정하려면 직접 상속이 더 편리하고 안전하다. 또는 es6에서 도입된 클래스를 사용해라!
console.log(test1 instanceof Test) //true
console.log(test1 instanceof Object) //
결론: 프로토타입 체인 상에 존재하는지 확인한다.
명시적으로 프로토타입을 지정하여 새로운 객체를 생성한다.
let TestObj = Object.create(프로토타입으로 지정할 객체)
let TestObj = Object.create(null) //암것도 안담김
console.dir(TestObj)
function Test(x) {
this.x = x;
}
Test.prototype.getAnswer = function () {
return
}
// 인스턴스 생성
const test1 = new Test(1)
let TestObj = Object.create(Test.prototype)
console.dir(TestObj)
new 연산자 없이도 객체를 생성, 프로토타입을 지정 하면서 객체를 생성가능, 객체 리터럴에 의해 생성된 객체도 상속받을 수 있다
결론: esLint 에서는 하지말라고 함
__proto__ 에 의한 직접 상속es6에서는 객체 리터럴 내부에서 __proto__ 접근자 프로퍼티를 사용하여 직접 상속을 구현할 수있다.
const Proto = {'어쩌구'}
const obj = {
저쩌구...,
__proto_: Proto
}
console.log(Object.getPrototypeOf(obj)===Proto) ; //true
생성자 함수로 인스턴스를 생성하지 않아도 참조/호출 할 수 있는 프로퍼티/메서드
정적 프로퍼티/메서드는 생성자 함수가 생성한 인스턴스로 참조/호출 할수없다.
function Test(x) {
this.x = x;
}
Test.prototype.getAnswer = function () {
return
}
Test.staticProp = 'static prop'
// 인스턴스 생성
const test1 = new Test(1)
test1.hasOwnProperty('static prop') //false
Test.hasOwnProperty('static prop') //true
결론: 정적 프로퍼티/메소드는 인스턴스로 참조/호출 불가!