Javascript/[인프런] 자바스크립트 중고급

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

minha62 2022. 2. 18. 04:16

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를 추가했으므로 호출할 수 있음
  5. return this.point;
    • 추가하더라도 this가 인스턴스를 참조
  6. 이런 특징을 활용하여
    • 상황(필요)에 따라 메소드를 추가
    • 역동적인 프로그램 개발 가능
  1.  

인스턴스 프로퍼티

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

4. obj.getPoint();

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

5. 인스턴스 프로퍼티는 공유되지 않음

 

6. Class 접근

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