new
키워드와 함께 실행 하는 함수를 생성자 함수라고 한다.
생성자 함수는 일반적으로 함수명을 '명사'로 짓고, 함수명의 첫 글자를 대문자로 표기한다.
function Person(name, age) {
this.name = name;
this.age = age;
}
const person1 = new Person("person1", 37);
console.log(person1); // Person {name: 'person1', age: 37}
자바스크립트에서 함수의 리턴값이 명시되지 않는다면 기본적으로 undefined가 반환된다.
하지만 아래 예제에서는 return값이 없는 함수임에도 특정객체가 반환되어 person1이라는 변수에 담기게 돈다.
function Person(name, age) {
this.name = name;
this.age = age;
}
const person1 = new Person("person1", 37);
console.log(person1); // Person {name: 'person1', age: 37}
왜 이럴까?
생성자 함수의 기본 반환값은 this
이기 때문이다.
또한 생성자 함수는 일반적으로 반환값을 명시하지 않는다.
모든 자바스크립트 객체는 생성자 함수를 이용해 만들어진다고 볼 수 있다.
아래와 같이 객체를 생성하는 방법은,
const obj = {};
const arr = [];
const func = function () {};
아래 방식과 동일하다.
const obj = new Object();
const arr = new Array();
const func = new Function();
여기서 Object, Array, Function은 js에 내장된 생성자 함수로, 첫번째 처럼 간결하게 작성해도 두번째와 같이 생성자함수를 이용해 만드는 것과 동일하게 객체를 생성한다.
각 함수가 소유한 prototype객체는 해당 함수가 생성자 함수로서 실행될 때 특정 역할을 하도록 설계되어 있다.
function foo() {
console.log("I am foo.");
}
const something = new foo();
이 예시에서 something이라는 변수에는 foo함수를 생성자 함수로 실행한 반환값, 즉 빈 객체가 담겨져있다.
console.log(something.hello); // undefined
그렇다면 콘솔 출력문에서 존재하지 않는 속성을 접근하기 때문에 undefined가 출력된다.
foo.prototype.hello = "Hello, World";
console.log(something.hello); // Hello, World
두번째 콘솔 출력문에서는 Hello, World
가 출력된다.
이것을 이해하기 위해서는 인스턴스에 대해 알아보자.
생성자 함수가 반환하는 빈 객체를 Instance (인스턴스)라고 부른다.
function Person(name) {
this.name = name;
}
const person1 = new Person("person1");
예를 들어, person1은 person의 인스턴스이다.
모든 인스턴스 객체는 해당 객체의 프로토타입에 존재하는 속성과 메소드에 접근해 사용할 수 있다.
function Person(name) {
this.name = name;
}
Person.prototype.age = 30;
const person1 = new Person("person1");
console.log(person1.age); // 30
생성자 함수 Person은 Person.prototype 객체와 함께 생성된다.
person1은 Person의 인스턴스로,
Person 생성자 함수를 통해 만들어졌기 때문에 Person.prototype에 존재하는 속성을 사용할 수 있다.
따라서 person.age를 출력하면 Person.prototype.age의 값을 출력한다.
function Person(name) {
this.name = name;
}
function Animal(breed) {
this.breed = breed;
}
Animal.prototype.age = 10;
const person1 = new Person("person1");
console.log(person1.age); // undefined
여기서 person1은 Person의 인스턴스이므로 Peron의 프로토타입을 상속한다. Animal의 프로토타입을 상속하지는 않으므로, Animal.prototype.age = 10
가 있더라도 person1.age
에는 아무런 영향을 받지 않는다. 따라서 undefined가 출력된다.
프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성된다.
생성자 함수는 두 가지로 구분할 수 있다.
각 생성자 함수 종류별 생성 시점에 대한 내용 추가 예정
객체 생성 방법은 다음과 같다.
모든 객체는 추상연산 OrdinaryObjectCreate에 의해 생성된다는 공통점이 있다.
필수적으로 자신이 생성할 객체의 프로토타입을 인수로 전달받는다.
빈 객체를 생성한 후 객체에 추가할 프로퍼티 목록이 인수로 전달된 경우에 프로퍼티를 객체에 추가한다. 그리고 인수로 전달받은 프로토타입을 자신이 생성한 객체의 [[prototype]] 내부 슬롯에 할당한 다음, 생성한 객체를 반환한다.
즉, 프로토타입은 추상연산 OrdinaryObjectCreate에 전달되는 인수에 의해 결정되는데, 이 인수는 객체가 생성되는 시점에 객체 생성 방식에 의해 결정된다.
객체리터럴을 평가해 객체를 생성할 때 js엔진이 추상연산을 호출한다. 추상연산 OrdinaryObjectCreate에 전달되는 프로토타입은 Object.prototype으로,
객체 리터럴에 의해 생성되는 객체의 프로토타입은 Object.prototype이다.따라서 Object.prototype의 프로퍼티와 메서드를 상속받아 사용할 수 있다.
Object 생성자 함수를 호출하면 똑같이 추상연산은 Object.prototype을 전달받는다.
생성자 함수를 호출하여 인스턴스를 생성하면
추상연산에 전달되는 프로토타입 (=생성자 함수에 의해 생성되는 객체의 프로토타입)은 생성자 함수의 prototype 프로퍼티에 바인딩되어 있는 객체다. (constructor property 참고)
function Person(name) {
this.name = name;
}
const me = new Person('Lee');
사용자 정의 생성자 함수 Person과 더불어 생성된 프로토타입 Person.prototype의 프로퍼티는 constructor뿐이다.
Object.create메서드는 명시적으로 프로토타입을 지정하여 새로운 객체를 생성한다. (다른 객체 생성방식과 마찬가지로 추상연산)
모든 프로토 타입은 constructor프로퍼티를 갖는다. 이것은 prototype프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리킨다.
constructor프로퍼티는 생성자 함수가 생성될 때 연결된다.
이 때 constructor 프로퍼티가 가리키는 생성자 함수는 인스턴스를 생성한 생성자 함수다.
자바스크립트는 객체의 프로퍼티에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype] 내부 슬롯의 참조를 따라 부모역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색한다. 이것을 프로토타입 체인이라 한다.
프로토타입 체인은 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 메커니즘이다.
프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면 인스턴스 프로퍼티로 추가한다.
이 때, 인스턴스 메서드 는 프로토타입 메서드를 오버라이딩했다고 한다.
이와 같이 상속관계에 의해 프로퍼티가 가려지는 현상을 프로퍼티 섀도잉이라고 한다.
@param {Object} prototype
@param {Object} [propertiesObject]
@return {Object}
Object.create(prototype[, propertiesObject])
Object.create메서드는 첫번째 매개변수에 전달한 객체의 프로토타입 체인에 속하는 객체를 생성한다.
즉, 객체를 생성하면서 직접적으로 상속을 구현한다.
이렇게 했을 때 장점은 무엇일까?
- new 연산자 없이 객체 생성
- 프로토타입을 지정하면서 객체 생성
- 객체 리터럴에 의해 생성된 객체도 상속받을 수 있음