관리 메뉴

흰둥씨의 개발장

[자바스크립트] 딥다이브) 생성자 함수에 의한 객체생성 본문

BoOk/JS deep dive

[자바스크립트] 딥다이브) 생성자 함수에 의한 객체생성

돈워리비해삐 2023. 6. 25. 04:44

생성자 함수?  new연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수 
ㄴJS는 Object, String, Number, Boolean, Function, Array, Date, RegExp, Promise...등의 빌트인 생성자 함수 제공
인스턴스 ? 생성자 함수에 의해 생성된 객체

1) Object 생성자 함수

ㄴnew연산자와 함께 Object생성자 함수를 호출하면 빈객체를 생성하여 반환
ㄴ빈객체 생성이후 프로퍼티 or 메서드 추가해서 객체완성가능

const people = new Object();  //생성자 함수 이용해서 빈객체 생성

people.name = 'Zzanggu';      //프로퍼티 추가 
people.sayhi = function () {  //메서드 추가 
	console.log('Hi, My name is ' + this.name);
    };
console.log(people); //{name: 'Zzanggu', sayhi: ƒ}
people.sayhi();      //Hi, My name is Zzanggu

 

 

2) 생성자 함수 

  - 1) 객체 리터럴에 의한 객체 생성 방식의 문제점 
ㄴ객체생성은 객체 리터럴 사용하는 것이 훨씬 간편하고 직관적
ㄴBUT, 리터럴방식은 단 하나의 객체만 생성
ㄴ동일 프로퍼티를 갖는 객체를 여러개 생성해야 하는 경우 매번 같은 프로퍼티를 기술해야하기 때문에 비효율적

const Obj = {}; //리터럴
const a = {
	name: '흰둥이',
    sayhi(){
    	return 'Hi ' + this.name;
    }
  }
  
const b = {
	name: '짱구',
    sayhi(){
    	return 'Hi ' + this.name;
    }
 
 console.log(a.sayhi()); //Hi 흰둥이
 console.log(b.sayhi()); //Hi 짱구

 

  - 2) 생성자 함수에 의한 객체생성의 장점
ㄴ프로퍼티 구조가 동일한 객체를 여러개 생성가능

function A(name){ //생성자 함수 
	this.name = name;       //생성자 함수 내부this는 생성자함수가 생성할 인스턴스임
    this.sayhi = function(){
    	return 'Hi ' + this.name;
        };
    }

//인스턴스 생성
const a = new A('흰둥이'); //name이 '흰둥이'인 객체 생성
const b = new A('짱구');  //name이 '짱구'인 객체 생성

console.log(a.sayhi());  //Hi 흰둥이
console.log(b.sayhi());  //Hi 짱구

 

**this ? 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수 (self-reference variable)
ㄴthis바인딩은 함수 호출 방식에 따라 동적으로 결정됨 

function merong () {
	console.log(this);
    }
    
 merong(); //window 일반 함수로서 호출 
 // 전역 객체는 브라우저 환경에서 window, Node.js환경에서는 global을 가리킴
 
 const obj = {merong};  //프로퍼티 축약표현 
 obj.merong(); // obj뜸 여기서의 this는 자신을 호출한 객체

 

  - 3) 생성자 함수의 인스턴스 생성 과정 
  ㄴ 1. 인스턴스 생성과 this바인딩 
       : 암묵적으로 빈객체 =인스턴스생성 => this에 인스턴스 바인딩 (런타임이전 실행됨)
  ㄴ2. 인스턴스 초기화 
       : 생성자 함수의 런타임에 인스턴스 초기화됨 (=인스턴스에 프로퍼티 메서드 추가, 생성자 함수의 인수 값 할당됨)
  ㄴ3. 인스턴스 반환 
       : 생성자 함수내에서 모든 처리 끝나면 객체가 바인딩된 this를 암묵적으로 반환함 (return 문이 있으면 this말고 return값을 반환)

 

  - 4) 내부 메서드 [[Call]]과 [[Construct]]
ㄴ함수객체는 일반객체에 있는 내부 슬롯, 내부메서드 가지고 있어서 일반적인 함수도 "생성자함수"로 호출가능 
ㄴ[[Call]]:  함수 호출시 호출되는 메서드 
ㄴ[[Construct]] : new연산자와 함께 생성자 함수로 호출시 호출되는 메서드
ㄴ하지만 모든 함수가 [[Construct]]갖고있지는 않음 

=>  즉, 함수객체는 모두 호출가능하면서, 생성자 함수로 호출할 수 있는것과, 생성자 함수로 호출할 수 없는 것이 있다. 

 

  - 5) constructor와 non-constructor의 구분 

constructor : 함수선언문, 함수 표현식, 클래스(클래스도 함수임)
non-constructor : 메서드, 화살표 함수
function ff(){} //함수선언문

const mf = function () {}; //함수 표현식

const mp = {
	x : function (){} // 프로퍼티x에 할당된 일반함수 (메서드로 인정하지 않음)
    }

new ff();   //constructor
new mf();   //constructor
new mp.x(); //constructor


const arrow = () => {} ; //화살표 함수 
new arrow();             //TypeError 화살표함수는 constructor아님


const obj = {
	x(){}  // 메서드 정의 : ES6의 메서드 축약 표현만 메서드로 인정함 
    };
new obj.x(); //TypeError 메서드는 constructor아님

ㄴ함수를 프로퍼티 값으로 사용하면 일반적으로 메서드로 통칭하지만,
ㄴECMAscript사양에서 메서드는 ES6의 메서드 축약 표현만을 의미함

 

 

  - 6) new 연산자 
ㄴ함수를 new연산자와 함께 호출시 해당 함수의 this는 생성자함수가 생성할 인스턴스를 의미 
ㄴ일반함수로서 호출하면 this는 전역 객체의미 
ㄴ일반 함수와 생성자 함수에는 형식적 차이없음 

function hi (name){  //생성자 함수 
	this.name = name;
    this.merong = function(){
   		return this.name + '😜'
        };
    }
    
 const callme = hi('짱구'); //일반함수로 호출됨
 console.log(callme);    //undefined
 
 console.log(name);    //짱구
 console.log(merong());  //짱구😜

function Add (x, y){
	return x + y;
    }

let Calc = new Add(); //일반함수를 new로 호출 

console.log(Calc);   // Add {}
//함수가 객체를 반환하지 않았으므로 반환문이 무시되고, 빈객체가 생성되어 반환됨 

function User ( name, role ){
	return {name, role};
    }

Calc = new User('짱구', '못말리는 역할');
console.log(Calc);   //{name: '짱구', role: '못말리는 역할'}

 

 

  - 7) new.target
생성자 함수가 new연산자 없이 호출되는 것을 막기 위해 ◆파스칼케이스를 쓰기도 하고, ◆ES6에서는 new.target지원함 
ㄴnew.target을 사용하면 생성자 함수로 호출했는지 알수있음
ㄴnew와 함께 생성자 함수가 호출되면 new.target은 함수 자신을 가리킴
ㄴnew없이 일반함수로 호출되면 new.target은 undefined
ㄴnew.target은 메타 프로퍼티라고 부름 
(IE는 new.target지원하지 않음)

function Circle(radius) {

	if(!new.target){  //new와 함께 호출되지 않으면 new.target은 undefined임
    	return new Circle(radius); //new안썼으면 다시 생성자함수로 new붙여서 재귀호출함 
        }
    
    this.radius = radius;
    this.getD = function(){
    	return 2 * this.radius;
        }
    }

const circle = Circle(5);  //new없이 호출해도 new.target을 통해 생성자 함수로 호출됨
console.log(circle.getD());  //10

ㄴnew.target을 못쓰는 IE라면... 스코프 세이프 생성자 패턴 사용할수있음 (page. 247을 펼치시오)