function 구분
- 빌트인 Function 오브젝트
- function 오브젝트: function 키워드로 생성
- function 인스턴스: new 연산자로 생성
빌트인 Function 오브젝트로 생성하기 때문에 function 오브젝트 또한 인스턴스입니다. 성격적으로는 인스턴스이지만 new 연산자로 생성한 인스턴스와 구분하기위해 function 오브젝트라 칭합니다.
new 연산자로 생성하는 인스턴스는 일반적으로 prototype에 프로퍼티를 작성합니다.
function Book(point) { this.point = point; }; Book.prototype.getPoint = function() { return this.point + 200; }; var obj = new Book(100); console.log(obj.point); console.log(obj.getPoint()); // 100 // 300
new연산자와 함께 인스턴스를 생성하는 함수로 new Book()에서 Book()이 생성자 함수입니다.
- new 연산자
인스턴스 생성을 제어하며 생성자 함수를 호출합니다.- 생성자 함수
인스턴스를 생성 및 반환하며 인스턴스에 초기값을 설정을 합니다. 코딩 관례로 생성자 함수의 첫 문자는 대문자를 사용합니다.
- Ex: new Number(), new Array(), new Book()
function Book(point) { this.point = point; }; Book.prototype.getPoint = function() { return this.point; }; var obj = new Book(10);
[[Construct]]
에 설정하였습니다. function Book(point) { this.point = point; }; Book.prototype.getPoint = function() { return this.point; }; var obj = new Book(10);
Book 인스턴스: { point: 10, __proto__ = { constructor: Book, // 외부 프로퍼티 getPoint: function(){}, __proto__: Object // 빌트인 Object 참조 } }
Book function 오브젝트: { prototype: { constructor: Book } }
생성하는 function 오브젝트를 참조합니다.
ES5
: constructor 변경이 불가능해 생성자를 활용할 수 없습니다.
ES6
: constructor 변경이 가능해져 활용성이 높아졌습니다.
var Book = function() {}; var result = Book === Book.prototype.constructor; console.log("1:", result); var obj = new Book(); console.log("2:", Book === obj.constructor); console.log("3:", typeof Book); console.log("4:", typeof obj); // 1:true // 2:true // 3:function // 4:object
function 오브젝트를 인스턴스로 생성하니 type이 object로 변경되었습니다. 그 이유는 [[Construct]]가 실행될 때 생성한 오브젝트의 [[Class]]에 'Object'를 설정하기 때문입니다.
이처럼 오브젝트 타입이 바뀐다는 것은 오브젝트의 성격과 목적이 바뀐다는것을 의미합니다. 그렇기에 일반적인 함수개념으로 접근하는 것이 아니라 인스턴스개념으로 접근해야 한다는 것입니다.
인스턴스의 가장 큰 특징은 prototype이 있으며 이 prototype에 많은 메소드들이 연결된다는 것입니다. 즉, 함수가 하나가 아닌 다수라는 의미입니다. function은 함수가 하나입니다. 하지만, 인스턴스는 함수가 다수입니다. 이런 특징의 차이로 인해 함수와 인스턴스는 다른 관점에서 봐야 합니다.
prototype 확장
prototype에 프로퍼티를 연결하여 prototype 확장
Book.prototype.getPoint = function () {...}
프로퍼티 공유
생성한 인스턴스에서 원본 prototype의 프로퍼티 공유
var obj = new Book(123); obj.getPoint();
예시처럼 인스턴스.메소드()를 호출하면 인스턴스의 getPoint를 호출하는 것이 아닌 Book.prototype.getPoint를 호출하는 것입니다.
여러 Book의 인스턴스 obj1,2,3,4가 있다면 서로가 하나의 원본(Book.prototype.getPoint)를 경로(path)만 가져와 참조하는 것입니다.
인스턴스 상속(Inheritance)
function 인스턴스를 연결하여 상속합니다.
Point.prototype = new Book();
ES5 인스턴스 상속
function Book(title) { this.title = title; }; Book.prototype.getTitle = function() { return this.title; }; function Point(title) { Book.call(this, title); }; Point.prototype = Object.create(Book.prototype, {}); var obj = new Point("javascript"); console.log(obj.getTitle()); // javascript
ES6 인스턴스 상속
class Book{ constructor(title) { this.title = title; } getTitle() { return this.title; } }; class Point extends Book { constructor(title) { super(title); } }; const obj = new Point("javascript"); console.log(obj.getTitle()); // javascript
prototype에 연결할 프로퍼티가 많을 때
Book.prototype.name1, 2, 3 ~ N형태는 Book.prototype을 반복 작성해야 하기 때문에 번거롭습니다.
그렇기에 Book.prototype = {name1: value, ...} 형태로 작성합니다.
하지만, 여기서 문제는 위와같이 prototype에 Object를 할당해버리면 constructor가 지워지게 됩니다.
constructor가 지워지는 문제와 대책
{name1: value, ...} 형태로 설정한 후 prototype에 constructor를 다시 연결합니다.
constructor 연결
function Book() {}; Book.prototype = { constructor: Book, setPoint: function() {} }; var obj = new Book(); console.log(obj.constructor);
function Book(point) { this.point= point; }; Book.prototype.getPoint = function() { return this.point; }; var obj = new Book(100); obj.getPoint();
obj: { point: 100, __proto__ = { constructor: Book, getPoint: function(){}, __proto__: Object } }
function Book() { console.log("1:", this.point); }; Book.prototype.getPoint = function() { this.setPoint(); console.log('2:", this.point); }; Book.prototype.setPoint = function() { this.point = 100; }; var obj = new Book(); obj.getPoint(); // 1:undefined // 2:100
function Book(point) { this.point = point; }; Book.prototype.getPoint = function() { return this.point }; var obj = new Book(100); console.log(obj.getPoint()); console.log(Book.prototype.getPoint()); // 100 // undefined
function Book() { this.point = 100; }; var obj = new Book(); console.log(obj.getPoint); Book.prototype.getPoint = function() { return this.point }; var result = obj.getPoint(); console.log(result);
function Book(point){ this.point = point; }; Book.prototype.getPoint(){ console.log(this.point); } var obj1 = new Book(100); var obj2 = new Book(200); var obj3 = new Book(300); obj1.getPoint(); obj2.getPoint(); obj3.getPoint(); // 100 // 200 // 300
생성자 함수에 메소드를 설정하는 것으로 만들어지는 모든 인스턴스의 __proto__
가 생성자 함수의 prototype에 경로 연결이 되어 해당 메소드를 사용 가능하게 됩니다.
obj 인스턴스 = { point: 100, getPoint: function(){ }, // ---(1) __proto__: { getPoint: function(){ } // ---(2) } }
prototpye에 연결된 프로퍼티도 인스턴스 프로퍼티(2)가 됩니다. 직접 인스턴스에 연결된 프로퍼티(1)와는 차이가 있습니다.
인스턴스의 프로퍼티(1)를 prototype으로 만든 인스턴스 프로퍼티(2)보다 먼저 사용합니다.
인스턴스마다 값을 다르게 가질수 있으며 인스턴스를 사용하는 중요한 목적입니다.
function Book(point) { this.point = point; }; Book.prototype.getPoint() { return 100; } var obj = new Book(200); obj.getPoint = function() { return this.point; }; console.log(obj.getPoint()); //200
obj 인스턴스 = { getPoint: function(){return this.point;}, __proto__:{ getPoint: function(){return 100;} } }