자바스크립트는 명령형, 함수형, 프로토타입 기반 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어이다.
따라서, 자바스크립트를 이루는 거의 "모든 것"이 객체이다. 원시 타입값을 제외한 나머지 값들(함수, 배열, 정규 표현식) 등은 모두 객체이다.
자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어있다. 이때의 부모 객체를 프로토타입객체 또는 줄여서 프로토타입이라 한다.
프로그램을 명령어 또는 함수의 목록으로 보는 전통적 명령형 프로그래밍의 절차지향적 관점에서 벗어나 객체(여러 개의 독립적 단위)의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임을 말한다.
//이름과 주소 속성을 갖는 객체
const person = {
name: 'Lee',
address: 'Seoul'
};
console.log(person);
const circle = {
radius: 5, //반지름
getDiameter() { //원의 지름
return 2 * this.radius;
},
getPerimeter() { //원의 둘레
return 2 * Math.PI * this.radius;
},
getArea() { //원의 넓이
return Math.PI * this.radius;
}
};
console.log(circle);
console.log(circle.getDiameter); //[Function: getDiameter]
console.log(circle.getPerimeter); //[Function: getPerimeter]
console.log(circle.getArea); //[Function: getArea]
//생성자함수
function Circle(radius) {
this.radius = radius;
this.getArea = function () {
return Math.PI * this.radius ** 2;
};
}
//반지름이 1인 인스턴스 생성
const circle1 = new Circle(1);
//반지름이 2인 인스턴스 생성
const circle2 = new Circle(2);
//Circle 생성자 함수는 인스턴스를 생성할 때마다 동일한 동작을 하는
//getArea 메서드를 중복 생성하고 모든 인스턴스가 중복 소유한다.
//getArea 메서드는 하나만 생성해 모든 인스턴스가 공유해서 사용하는 것이 바람직
console.log(circle1.getArea === circle2.getArea); //false
=> 상속을 통해 불필요한 중복 제거
function Circle(radius) {
this.radius = radius;
}
//Circle 생성자함수가 생성한 모든 인스턴스가 getArea 메서드를
//공유해 사용할수 있도록 프로토타입에 추가한다.
//프로토타입은 Circle 생성자함수의 prototype프로퍼티에 바인딩되어있다.
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
Circle 생성자 함수의 인스턴스는 부모인 Circle.prototype의 모든 프로퍼티와 메서드를 상속받는다.
=> radiuss 프로퍼티만 개별 소유하고 공통 메서드는 상속받아서 사용해 코드 재사용성 높아짐
1 proto는 접근자 프로퍼티다.
const obj = {};
const parent = { x: 1 };
//getter 함수인 get __proto__가 호출되어 obj 객체의 프로토타입을 취득
obj.__proto__;
//setter 함수인 set __proto__가 호출되어 obj 객체의 프로토타입을 교체
obj.__proto__ = parent;
console.log(obj.x); //1
2 proto 접근자 프로퍼티는 상속을 통해 사용된다.
const person = { name: 'Lee' };
//person 객체는 __proto__ 프로퍼티를 소유하지 않는다.
console.log(person.hasOwnProperty('__proto__')); //false
//__proto__ 프로퍼티는 모든 객체의 프로토타입 객체인 Object.prototype 의 접근자 프로퍼티다.
console.log(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__'));
/* { get: [Function: get __proto__], set: [Function: set __proto__],
enumerable: false, configurable: true} */
//모든 객체는 Object.prototype의 접근자 프로퍼티 __proto__를 상속받아 사용할 수 있다.
console.log({}.__proto__ === Object.prototype); //true
3 proto 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유
: 상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해서
const parent = {};
const child = {};
child.__proto__ = parent;
parent.__proto__ = child; //TypeError: Cyclic __proto__ value
프로토타입은 단방향 링크드 리스트로 구현되어야 한다. 즉, 검색방향이 한쪽으로만 흘러가야 한다. 그렇지 않으면 체인 종점이 존재하지 않기 때문에 무한 루프에 빠지게 된다.
4 proto 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다.
//obj는 프로토타입 체인의 종점. 따라서 Object.__proto__를 상속받을 수 없다.
const obj = Object.create(null);
//obj는 Object.__proto__를 상속받을 수 없다.
console.log(obj.__proto__);
//-> __proto__ 보다 Object.getPrototypeOf 메서드를 사용하는 편이 좋다.
console.log(Object.getPrototypeOf(obj));
프로토타입을 교체하고 싶은 경우에는 Object.setPrototypeOf 메서드를 사용할 것이 권장된다.
const obj = {};
const parent = { x: 1 };
//obj 객체의 프로토타입을 취득
Object.getPrototypeOf(obj); //obj.__proto__
//obj 객체의 프로토타입을 교체
Object.setPrototypeOf(obj); //obj.__proto__ = parent;
console.log(obj.x); //1
함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.
//함수 객체는 프로토타입 프로퍼티 소유
(function () { }).hasOwnProperty('prototype'); //true
//일반 객체는 프로토타입 프로퍼티 무소유
({}).hasOwnProperty('propertype') //false
prototype 프로퍼티는 생성자 함수가 생설할 객체(인스턴스)의 프로토타입을 가리킨다.
//1. 화살표 함수는 non-constructor
const Person = name => {
this.name = name;
}
//non-constructor는 prototype 프로퍼티를 소유하지 않는다.
console.log(Person.hasOwnProperty('prototype')); //false
//non-constructor는 프로토타입을 생성하지 않는다.
console.log(Person.prototype); //undefined
//2. 메서드 축약 표현으로 정의한 메서드는 non-constructor이다.
const obj = {
foo() { }
};
console.log(obj.foo.hasOwnProperty('prototype')); //false
console.log(obj.foo.prototype); //false
__proto__접근자 프로퍼티
: 객체가 자신의 프로토타입에 접근 또는 교체하기 위해 사용prototype 프로퍼티
: 생성자 함수가 자신이 생성할 객체(인스턴스)의 프로토타입을 할당하기 위해 사용
function Person(name) {
this.name = name;
}
const me = new Person(('Kook'));
//Person.protytope과 me.__proto__는 동일한 프로토타입을 가리키다.
console.log(Person.prototype === me.__proto__); //true
모든 프로토타입은 constructor 프로퍼티를 갖는다. 이 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리키며, 이 연결은 생성자 함수가 생성될 때, 즉 함수 객체가 생성될 때 이뤄진다.
function Person(name) {
this.name = name;
}
const you = new Person('Lee');
console.log(me.constructor === Person); //true
you객체가 constructor 프로퍼티를 통해 생성자 함수와 연결되고, you 객체는 Person.prototype(부모)에서 constructor 프로퍼티를 상속받아 사용할 수 있다.
생성자 함수에 의해 생성된 인스턴스는 프로토타입의 constructor 프로퍼티에 의해 생성자 함수와 연결된다. 이떄 constructor 프로퍼티가 가리키는 생성자 함수는 인스턴스를 생성한 생성자 함수다.
//obj 객체를 생성한 생성자 함수는 Object다
const obj = new Object();
console.log(obj.constructor === Object); //true
//add 함수 객체를 생성한 생성자 함수는 Function이다.
const add = new Function('a', 'b', 'return a + b');
console.log(add.constructor === Function);
//생성자함수
function Person(name){
this.name = name;
}
constructor 속성 : 어떤 생성자 객체를 통해 생겨난 인스턴스 인지를 알려주는 역할. 즉 생성자 객체를 반환한다.
//객체 리터럴
const obj = {};
//함수 리터럴
const add = function (a, b) { return a + b };
//배열 리터럴
const arr = [1, 2, 3]
//정규 표현식 리터럴
const regexp = /is/ig;
리터럴 표기법에 의해 생성된 객체도 물론 프로토타입이 존재. 하지만 이 때 생성된 객체의 생성자 함수가 반드시 객체를 생성한 생성자 함수라고는 단정할 수는 없다.
//obj 객체는 Object 생성자 함수로 생성한 객체가 아니라 객체 리터럴로 생성했다.
const obj = {};
// 하지만 obj 객체의 생성자 함수는 Object 생성자 함수이다.
console.log(obj.constructor === Object); //true
결론: 프로토타입의 constructor 프로퍼티를 통해 연결되어있는 생성자 함수를 리터럴 표기법으로 생성한 객체를 생성한 생성자 함수
로 생각해도 무리는 없다.
출처: https://velog.io/@minj9_6/JavaScript-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%8502
리즈님 저 스터디 방장입니다!
힘든거 때문이시라면 다시 들어오세요 여태까지 잘하셨어요 지금 포기하기엔 아쉽네요