[자바스크립트 중고급] 5. function instance

Speedwell🍀·2022년 2월 1일
0

function 인스턴스 기준

function Book(point){
  this.point = point;
};
Book.prototype.getPoint = function(){
  return this.point + 200;
};
var obj = new Book(100);
obj.getPoint();
  • function 구분

    • 빌트인 Function 오브젝트
    • function 오브젝트: function 키워드로 생성
    • function 인스턴스: new 연산자로 생성
  • function 오브젝트도 인스턴스

    • 빌트인 Function 오브젝트로 생성하기 때문
    • 성격적으로는 인스턴스이지만
    • new 연산자로 생성한 인스턴스와 구분하기 위해 강좌에서는 function 오브젝트로 표기
  • new 연산자로 생성하는 인스턴스는

    • 일반적으로 prototype에 프로퍼티를 작성

function 인스턴스 생성

function Book(point){
  this.point = point;
};
Book.prototype.getPoint = function(){
  return this.point + 200;
};
var obj = new Book(100);
log(obj.point);
log(obj.getPoint());

// 100
// 300

위의 코드는 function 인스턴스를 생성하는 전형적인 형태

인스턴스 생성 순서, 방법

  1. function Book(point){...}

    • Book 오브젝트를 생성
    • Book.prototype이 만들어짐
  2. Book.prototype.getPoint = function(){}

    • Book.prototype에 getPoint를 연결하고 function(){}을 할당
    • Book.prototype이 오브젝트이므로 프로퍼티를 연결할 수 있음
  3. var obj = new Book(100);

    • Book()을 실행하며 인스턴스를 생성하고 생성한 인스턴스에 point 값을 설정
    • Book.prototype에 연결된 프로퍼티를 생성한 인스턴스에 할당
  4. console.log(obj.point);

    • obj 인스턴스에서 프로퍼티 이름으로 값을 구해 출력
  5. console.log(obj.getPoint());

    • obj 인스턴스의 메소드를 호출
  6. return this.point + 200;에서

    • this가 obj 인스턴스를 참조
  7. 강좌의 함수/메소드 사용 기준

    • Book(): 함수
    • getPoint(): 메소드, prototype에 연결

생성자 함수

  • new 연산자와 함께 인스턴스를 생성하는 함수

    • new Book()에서 Book()이 생성자 함수
  • new 연산자

    • 인스턴스 생성을 제어
    • 생성자 함수 호출
  • 생성자 함수

    • 인스턴스 생성, 반환
    • 인스턴스에 초기값 설정
  • 코딩 관례로 생성자 함수의 첫 문자는 대문자

    • new Number(), new Array(), new Book()

생성자 함수 실행 과정

function Book(point){
  this.point = point;
};
Book.prototype.getPoint = function(){
  return this.point;
};
var obj = new Book(10);

new 연산자로 인스턴스 생성을 제어하고, 생성자 함수인 Book()으로 인스턴스를 생성하여 반환

new와 생성자 함수 실행 과정

  1. 엔진이 new 연산자를 만나면

    • function의 [[Construct]]를 호출하면서 파라미터 값으로 10을 넘겨줌
  2. function 오브젝트를 생성할 때

    • Book() 함수 전체를 참조하도록 [[Construct]]에 설정함
  3. [[Construct]]에서 인스턴스를 생성하여 반환

  4. 반환된 인스턴스를 new 연산자가 받아

    • new 연산자를 호출한 곳으로 반환
  5. new라는 뉘앙스로 인해

    • new 연산자가 인스턴스를 생성하는 것으로 생각할 수 있지만
    • function 오브젝트의 [[Construct]]가 인스턴스를 생성
    • 그래서 Book()이 생성자 함수

인스턴스 생성 과정

function Book(point){
  this.point = point;
};
Book.prototype.getPoint = function(){
  return this.point;
};
var bookObj = new Book(10);
Book 인스턴스: {
	point: 10,
    __proto__ = {
    	constructor: Book,
        getPoint: function(){},
        __proto__: Object
	}
}
  1. new Book(10) 실행

    • Book 오브젝트의 [[Construct]] 호출
    • 파라미터 값을 [[Construct]]로 넘겨줌
  2. 빈 Object를 생성

    • 이것이 인스턴스
    • 지금은 빈 오브젝트{ }이며 이제부터 하나씩 채워감
  3. 오브젝트에 내부 처리용 프로퍼티를 설정

    • 공통 프로퍼티와 선택적 프로퍼티
  4. 오브젝트의 [[Class]]에 "Object" 설정

    • 따라서 생서한 인스턴스 타입은 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;
log("1:", result);

var obj = new Book();
log("2:", Book === obj.constructor);

log("3:", typeof Book);
log("4:", typeof obj);

// 1:true
// 2:true
// 3:function
// 4:object
  1. Book === Book.prototype.constructor;

    • [실행결과] 1번에 true가 출력된 것은
    • Book 오브젝트와 Book.prototype.constructor가 타입까지 같다는 뜻
    • Book 오브젝트를 생성할 때 Book.prototype.constructor가 Book 오브젝트를 참조하기 때문
  2. Book === obj.constructor;

    • obj의 constructor가 Book 오브젝트를 참조하므로
    • [실행결과] 2번에 true가 출력됨
  3. typeof Book;

    • Book 오브젝트의 타입은 function
  4. typeof obj;

    • obj 인스턴스 타입은 object
  5. function 오브젝트를 인스턴스로 생성했더니

    • object로 타입이 변경됨
    • 이것은 [[Construct]]가 실행될 때 생성한 오브젝트의 [[Class]]에 'Object'를 설정하기 때문
  6. 오브젝트 타입이 바뀐다는 것은

    • 오브젝트 성격과 목적이 바뀐 것을 뜻함
    • 다른 관점에서 접근해야 함

prototype 오브젝트 목적

  • prototype 확장

    • prototype에 프로퍼티를 연결하여 prototype 확장
    • Book.prototype.getPoint = function(){}
  • 프로퍼티 공유

    • 생성한 인스턴스에서 원본 prototype의 프로퍼티 공유
    • var obj = new Book(123); obj.getPoint;
  • 인스턴스 상속(Inheritance)

    • function 인스턴스를 연결하여 상속
    • Point.prototype = new Book();

인스턴스 상속

  • 인스턴스 상속 방법
    • prototype에 연결된 프로퍼티로 인스턴스를 생성하여 상속받을 prototype에 연결
    • 그래서 prototype-based 상속이라고도 함
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("자바스크립트");
log(obj.getTitle());

// 자바스크립트
  • JS에서 prototype은 상속보다

    • 프로퍼티 연결이 의미가 더 큼
    • 인스턴스 연결도 프로퍼티 연결의 하나
  • ES5 상속은 OOP의 상속 기능 부족

    • ES6의 Class로 상속 사용
class Book{
  constructor(title){
    this.title = title;
  };
  getTitle(){
    return this.title;
  }
};
class Point extends Book{
  constructor(title){
    super(title);
  };
};
const obj = new Point("자바스크립트");
log(obj.getTitle());

// 자바스크립트

prototype 확장 방법

  • prototype에 프로퍼티를 연결하여 작성

    • prototype.name = value 형태
  • name에 프로퍼티 이름 작성

  • value에 JS 데이터 타입 작성

    • 일반적으로 function을 사용
  • prototype에 null을 설정하면 확장 불가


프로퍼티 연결 고려사항

  • prototype에 연결할 프로퍼티가 많을 때
    • Book.prototype.name1, 2, 3 ~ N 형태는 Book.prototype을 반복해서 작성해야 하므로 번거로움
    • Book.prototype = {name1: value, ...} 형태로 작성
  • constructor가 지워지는 문제와 대책
    • {name1: value, ...} 형태로 설정한 후
    • prototype에 constructor를 다시 연결

constructor 연결

function Book(){};
Book.prototype = {
  constructor: Book,
  setPoint: function(){}
};
var obj = new Book();
log(obj.constructor);

// function Book(){}
  1. 오브젝트 리터럴{}을 사용하여

    • 프로퍼티를 연결할 때에는
    • constructor가 지워지는 것을 고려해야 함
  2. constructor가 없어도 인스턴스가 생성되지만

    • constructor가 연결된 것이 정상이므로
    • 코드처럼 constructor에 Book function을 할당

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
	}
}

prototype 확장

  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(){
  log("1:", this.point);
};
Book.prototype.getPoint = function(){
  this.setPoint();
  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가 메소드를 호출한 인스턴스 참조
    • 즉, 메소드 앞에 작성한 인스턴스 참조
  • 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);
log(obj.getPoint());

log(Book.prototype.getPoint());

// 100 
// undefined
  • Book.prototype.getPoint();

    • 인스턴스를 생성하지 않고 직접 메소드 호출
  • Book.prototype을

    • getPoint()에서 this로 참조
  • obj 인스턴스에는 point가 있지만

    • Book.prototype에 point가 없으므로 undefined를 반환
    • 인스턴스를 생성하여 메소드를 호출하는 것과 직접 prototype을 작성하여 호출하는 것의 차이

prototype 프로퍼티 공유 시점

  • 사용하는 시점에 prototype의 프로퍼티 공유

  • prototype의 프로퍼티로 인스턴스를 생성하지만

    • 인스턴스의 프로퍼티는 원본 prototype의 프로퍼티를 참조
    • 복사하여 인스턴스에 갖고 있는 개념이 아님
  • 인스턴스의 메소드를 호출하면

    • 원본 prototype의 메소드를 호출
  • 원본 prototype에 메소들르 추가하면

    • 생성된 모든 인스턴스에서 추가한 메소드 사용 가능
    • 원본 prototype의 메소드를 호출하기 때문

function Book(){
  this.point = 100;
};
var obj = new Book();
log(obj.getPoint);

Book.prototype.getPoint = function(){
  return this.point;
};
var result = obj.getPont();
log(result);

// undefined
// 100
  1. var obj = new Book();

    • 인스턴스를 생성하여 obj에 할당
  2. console.log(obj.getPoint);

    • obj 인스턴스에 getPoint()가 없음
  3. Book.prototype.getPoint = function(){return this.point;};

    • Book.prototype에 getPoint() 추가
    • 앞에서 생성한 obj 인스턴스에서 getPoint()를 사용할 수 있음
  4. var result = obj.getPoint();

    • 인스턴스를 생성할 때는 obj에 getPoint가 없었지만
    • getPoint()를 호출하기 전에 Book.prototype에 getPoint를 추가했으므로 호출할 수 있음
  1. return this.point;

    • 추가하더라도 this가 인스턴스를 참조
  2. 이런 특징을 활용하여

    • 상황(필요)에 따라 메소드를 추가
    • 역동적인 프로그램 개발 가능

인스턴스 프로퍼티

obj 인스턴스 = {
	point: 100,
    getPoint: function(){},
    __proto__: {
    	getPoint: function(){}
	}
}
  • prototype에 연결된 프로퍼티도

    • 인스턴스 프로퍼티가 됨
    • 직접 인스턴스에 연결된 프로퍼티와 차이 있음
  • 인스턴스의 프로퍼티를

    • prototype으로 만든 인스턴스 프로퍼티보다 먼저 사용
  • 인스턴스마다 값을 다르게 가질 수 있음

    • 인스턴스를 사용하는 중요한 목적

인스턴스 프로퍼티 우선 사용

function Book(point){
  this.point = point;
};
Book.prototype.getPoint = function(){
  return 100;
};
var obj = new Book(200);

obj getPoint = function(){
  return this.point;
};
log(obj.getPoint());

// 200
  1. Book.prototype.getPoint = function(){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;}
	}
}
  1. obj.getPoint();

    • obj 인스턴스의 getPoint() 호출
    • prototype의 getPoint()가 호출되지 않고 인스턴스의 getPoint()가 호출됨
    • 인스턴스에 연결한 프로퍼티를 먼저 사용하기 때문
  2. 인스턴스 프로퍼티는 공유되지 않음

  3. Class 접근

    • 설계가 중요
    • OOP 개념 이해 필요

0개의 댓글