[JS] #6 Function Instance

simoniful·2021년 4월 23일
0

ES5 JS - Advanced

목록 보기
6/8
post-thumbnail

개요

기준

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
  1. function Book(point){...}
    • Book 오브젝트를 생성합니다. 엔진이 자동으로 Book.prototype을 만듭니다.
  2. Book.prototype.getPoint = function() {...}
    • Book.prototype이 오브젝트이므로 프로퍼티로 getPoint(이름)를 연결하고 function() {...}(값)을 할당
  3. var obj = new Book(100);
    • Book()을 실행하며 인스턴스를 생성하고, 생성한 인스턴스에 함수 코드를 실행하여 point값을 설정합니다. 이 때 this는 만들어진 인스턴스를 참조합니다.
    • Book.prototype에 연결된 프로퍼티를 생성한 인스턴스에 할당합니다.
  4. console.log(obj.point);
    • obj 인스턴스에서 프로퍼티 이름으로 값을 구해 출력
  5. console.log(obj.getPoint());
    • obj인스턴스의 메소드를 호출합니다.
    • return this.point + 200; 에서 this가 obj인스턴스를 참조합니다.
  6. 함수/메소드 사용 기준
    • Book(): 함수
    • getPoint(): 메소드, prototype에 연결

생성자 함수

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);
  1. 엔진이 new 연산자를 만나면 function 의 내부 프로퍼티인 [[Construct]]를 호출하면서 파라미터 값으로 10을 넘겨 줍니다.
  2. function 오브젝트를 생성할 때 Book()함수 전체를 참조하도록 [[Construct]]에 설정하였습니다.
  3. [[Construct]]에서 인스턴스를 생성하여 반환합니다.
  4. 반환된 인스턴스를 new 연산자가 받아 호출한 곳으로 반환합니다.
  5. new라는 뉘앙스로 인해 new 연산자가 인스턴스를 생성하는 것으로 생각할 수 있지만, function 오브젝트의 [[Construct]]가 인스턴스를 생성합니다. 그렇기에 Book()이 생성자 함수입니다.

인스턴스 생성과정

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 참조
  }
}
  1. new Book(10) 실행
    ⇒ Book 오브젝트의 [[Construct]]가 호출되고 파라미터 값을 [[Construct]]로 넘겨줍니다.
  2. 빈 Object를 생성하는데, 이것이 인스턴스이고 이 빈 오브젝트{ }를 하나씩 채워갑니다.
  3. 오브젝트에 내부 처리용 프로퍼티를 설정합니다.
  4. 오브젝트의 [[Class]]에 "Object"설정을 하기에 생성한 인스턴스 타입은 Object입니다. 그래서 Function이아닌 Object타입으로 나오는 것입니다.
  5. Book.prototype에 연결된 프로퍼티(메소드)를 생성한 인스턴스의 [[Prototype]]에 설정하며 외부 프로퍼티인 constructor도 같이 설정됩니다.

Constructor

Book function 오브젝트: {
  prototype: {
    constructor: Book
  }
}

생성하는 function 오브젝트를 참조합니다.

  • function 오브젝트를 생성할 때 설정하며 prototype에 연결되어 있습니다.
  • constructor가 없어도 인스턴스가 생성되지만 필요하지 않다는 의미는 아닙니다.

ES5: constructor 변경이 불가능해 생성자를 활용할 수 없습니다.
ES6: constructor 변경이 가능해져 활용성이 높아졌습니다.

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
  1. Book === Book.prototype.constructor
    • Book 오브젝트와 Book.prototype.constructor는 타입까지 동일합니다.
    • constructor 프로퍼티는 생성하는 function 오브젝트(Book)을 참조하기 때문입니다.
  2. Book === obj.constructor;
    • obj의 constructor가 Book 오브젝트를 참조하기 때문에 마찮가지로 true가 출력됩니다.
  3. typeof Book
    • Book 오브젝트의 타입은 function 타입입니다.
  4. typeof obj
    • obj 인스턴스의 타입은 object입니다.

function 오브젝트를 인스턴스로 생성하니 type이 object로 변경되었습니다. 그 이유는 [[Construct]]가 실행될 때 생성한 오브젝트의 [[Class]]에 'Object'를 설정하기 때문입니다.

이처럼 오브젝트 타입이 바뀐다는 것은 오브젝트의 성격과 목적이 바뀐다는것을 의미합니다. 그렇기에 일반적인 함수개념으로 접근하는 것이 아니라 인스턴스개념으로 접근해야 한다는 것입니다.

인스턴스의 가장 큰 특징은 prototype이 있으며 이 prototype에 많은 메소드들이 연결된다는 것입니다. 즉, 함수가 하나가 아닌 다수라는 의미입니다. function은 함수가 하나입니다. 하지만, 인스턴스는 함수가 다수입니다. 이런 특징의 차이로 인해 함수와 인스턴스는 다른 관점에서 봐야 합니다.


prototype

prototype 오브젝트 목적

  • 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();

인스턴스 상속

  • 인스턴스 상속 방법
    • prototype에 연결된 프로퍼티로 인스턴스를 생성하여 상속받을 prototype에 연결합니다.
    • prototype-based 상속이라고도 부릅니다.
  • JS에서 prototype은 상속보다는 프로퍼티 연결의 의미가 더 큽니다. 인스턴스 연결도 프로퍼티 연결의 하나입니다.
  • ES5상속은 OOP의 상속기능이 부족합니다.
    • ES6에서는 Class로 상속을 할 수 있습니다. 하지만, 개발자가 Java에서 상속하듯이 키워드(class, extends, super...)를 사용해 상속을 하지만 그 내부 코드와 로직은 기존과 크게 다르지 않습니다.

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 확장 방법

  • prototype에 프로퍼티를 연결하여 작성합니다.
    • prototype.name = value 형태
  • name에 프로퍼티 이름을 작성합니다
  • value에 JS데이터 타입을 작성합니다.
    • 일반적으로 function을 사용
  • prototype에 null을 설정하면 확장이 불가능해집니다.

프로퍼티 연결 고려 사항

  • 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);
  1. 오브젝트 리터럴{}을 사용하여 프로퍼티를 연결할 때는 constructor가 지워지는 것을 고려해야 합니다.
  2. constructor가 없어도 인스턴스가 생성되지만, constructor가 연결된 상태가 정상이므로 생성자 함수를 constructor에 할당해야 합니다.

prototype 확장과 인스턴스 형태

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
  }
}
  1. function Book(point){};
    Book 오브젝트 생성
  2. Book.prototype.getPoint = function(){}
    Book.prototype에 getPoint 메소드 연결
  3. var obj = new Book(100);
    인스턴스를 생성하여 obj에 할당
  4. obj.getPoint()
    obj 인스턴스의 getPoint() 호출
  5. 인스턴스를 생성하면 prototype에 연결된 메소드를 인스턴스.메소드 이름() 형태로 호출합니다.

this와 prototype

this로 인스턴스 참조

  • this로 메소드를 호출한 인스턴스 참조
    • var obj = new Book();
    • obj.get() 형태에서 this로 obj 참조합니다.
  • 인스턴스에서 메소드 호출 방법
    • prototype에 연결된 프로퍼티가 __proto__에 설정되며 인스턴스 프로퍼티가 됩니다.
    • this.prototype.setPoint()형태가 아닌 this.setPoint()형태로 호출합니다.

this와 prototype의 관계

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
  • console.log("1:", this.point);
    • 생성자 함수에서 this는 생성하는 인스턴스 참조
    • 생성하는 인스턴스에 point 프로퍼티가 없더라도 에러가 나지 않고 undefined를 반환(변수는 에러 반환)
  • obj.getPoint()
    • this가 메소드를 호출한 인스턴스 참조
    • 즉, 메소드 앞에 작성한 인스턴스 참조(obj)
  • this.setPoint()
    • this가 인스턴스 참조하며 인스턴스에 있는 setPoint()호출
  • this.point = 100;
    • this가 인스턴스를 참조하며 인스턴스의 point프로퍼티에 100을 할당

prototype 메소드 직접 호출

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
  • Book.prototype.getPoint();
    • 인스턴스를 생성하지 않고 직접 메소드 호출
  • Book.prototype을 getPoint에서 this로 참조
  • obj 인스턴스에는 point가 있지만
    • Book.prototype에 point가 없으므로 undefined를 반환합니다.
    • 인스턴스를 생성하여 메소드를 호출하는 것과 직접 prototype을 작성하여 호출하는 것의 차이입니다.
    • 직접 작성할 때는 call, bind, apply 사용해서 this가 지정한 오브젝트를 참조하도록하여 사용합니다.

prototype 프로퍼티 공유 시점

  • 사용하는 시점에 prototype의 프로퍼티를 공유합니다.
  • prototype의 프로퍼티로 인스턴스를 생성하지만 인스턴스의 프로퍼티는 원본 prototype의 프로퍼티를 참조합니다.
  • 복사하여 인스턴스에 갖고 있는 개념이 아닙니다.
  • 인스턴스의 메소드를 호출하면 원본 prototype의 메소드를 호출합니다.
  • 원본 prototype에 메소드를 추가하면 생성된 모든 인스턴스에서 추가한 메소드를 사용 가능합니다. 이는 원본 prototype의 메소드를 호출하기 때문입니다.

공유과정

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);
  1. var obj = new Book();
    인스턴스를 생성하여 obj에 할당합니다.
  2. console.log(obj.getPoint);
    obj 인스턴스에 getPoint()가 없기에 undefined가 출력됩니다.
  3. Book.prototype.getPoint = function(){ return this.point };
    Book.prototype에 getPoint()를 추가합니다. 이제 앞에서 생성한 obj 인스턴스에서도 getPoint()를 사용가능합니다.
  4. var result = obj.getPoint();
    인스턴스를 생성하는 시점에는 obj에 getPoint가 없었지만, 위에서 prototype에 getPoint를 추가했기에 이제 호출이 가능해집니다.
  5. return this.point
    추가하더라도 this가 인스턴스를 참조합니다. 호출하는 시점에 obj가 this 바인딩 컴포넌트에 바인딩됩니다.
  6. 이런 특징을 이용해 상황에 따라 메소드를 추가 하는 식의 동적 프로그램 개발이 가능해집니다.
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
  1. Book.prototype.getPoint(){return 100;}
    prototype에 getPoint를 연결하며 인스턴스의 getPoint()호출 시 100을 반환합니다.
  2. obj.getPoint = function(){ return this.point; };
    생성한 인스턴스에 getPoint를 연결합니다.
    함수가 호출되면 200을 반환합니다.
  3. obj 인스턴스 구성 형태
    obj 인스턴스 = {
      getPoint: function(){return this.point;},
      __proto__:{
        getPoint: function(){return 100;}
      }
    }
  4. obj.getPoint();
    • obj 인스턴스의 getPoint() 호출
    • prototype의 getPoint()가 호출되지 않고 인스턴스의 getPoint()가 호출됩니다.
    • 인스턴스의 연결한 프로퍼티를 먼저 사용하기 때문입니다.
  5. 인스턴스 프로퍼티는 공유되지 않습니다. 프로토타입에 있는 것만 공유됩니다.
  6. Class 접근
    설계가 중요하며 객체지향 프로그래밍 개념 이해 필요
    인스턴스는 메소드, 프로퍼티의 묶음이며, 이는 데이터 중심으로 접근하여 인스턴스마다 메소드를 통한 값을 가져가는 것이 목적
profile
소신있게 정진합니다.

0개의 댓글