생성자함수내 this 바인딩

lee jae hwan·2022년 7월 29일

javascript

목록 보기
61/107

생성자함수에는 생성객체의 프로퍼티를 설정하기위해 this를 사용한다.

function User(name) {
  this.name = name;
  this.sayHi = function(){
    console.log(this.name);
  };
}
let user = new User("보라");
user.sayHi();

new지시자는 빈객체 생성후 본문에 있는 this프로퍼티를 생성객체에 바인딩후 객체를 반환하는 역활을 한다.

user.sayHi();
속성접근자는 user객체에 sayHi프로퍼티가 연결하고 ()호출연산자는 메소드를 호출한다. sayHi는 일반함수이므로 자신의 this를 가지며 자신함수와 연결된 객체를 this로 참조한다.


function User(name) {
  this.name = name;
  this.sayHi = ()=> console.log(this.name)
}
let user = new User("보라");
user.sayHi();

sayHi가 화살표함수로 선언되어있다.

user.sayHi();
화살표함수내 this를 평가하기 위해서는 가장 가까운 일반함수를 찾으야하므로 생성자함수를 찾는다.

생성자함수는 전역객체와 연결될까? 생성객체와 연결될까?

일반함수라면 전역객체와 연결되겠지만 new지시자로 인해 생성자함수는 생성객체와 연결되어 생성자함수내 this는 생성객체와 바인딩된다.



function User(name) {
  this.name = name;
  this.sayHi = ()=> console.log(this.name)
}
let user = new User("보라");
setTimeout(user.sayHi, 1000);  // 보라

user.sayHi가 일반함수로 구현되었다면 함수가 값으로 전달되어 호출될때 전역객체와 연결되어 호출된다.

user.sayHi가 화살표로 구현되어 객체가 생성될때 this가 생성객체로 바인딩된다.

다시한번 말하지만 바인딩되면 내부에 this는 없는 것과 같다.

따라서 setTimeout에는 바인딩된 user.sayHi메소드가 전달되어 정상적으로 출력된다.


자바스크립트는 일반함수와 생성자함수의 기술적차이가 없다고 여러번 언급했다.

생성자함수라서 화살표함수가 생성시 this바인딩이 일어나는 것이 아니고 일반함수인 경우에도 같이 적용되는 규칙이다.

단 생성자함수와 일반함수의 차이는 생성자함수는 화살표내 this가 생성객체에 바인딩되고 일반함수는 화살표내 this가 일반함수에 연결객체에 바인딩되는 차이가 있는 것이다.



class경우도 보자

class Button {
  constructor(value) {
    this.value = value;
  }
  click = () => {
    console.log(this.value); // 객체생성시 바인딩
  }
  fn(){
    console.log(this.value); // 런타임 바인딩
  }
}

let button = new Button("안녕하세요.");
console.log( Button.prototype.click === button.click ); // false
console.log( Button.prototype.fn === button.fn  ); // true

생성자함수내 화살표함수의 this바인딩 규칙은 class에도 똑같이 적용된다.

click = () => { console.log(this.value); };
click프로퍼티가 객체에 생성될때 화살표함수이기때문에 this가 바인딩된다.

click은 클래스필드로 객체에 추가되는 프로퍼티다.
클래스필드는 constructor가 실행되기 바로 전에 객체 프로퍼티로 추가된다.





생성자함수로 1초마다 시간출력하기 구현

function Clock({template}){  
  
  // 생성자함수내에서 정의된 함수는 객체마다 생성되므로 
  // 객체크기가 커진다.
  // Clock.prototype에 정의하면 공유한다.
  function getNow(){ // 현재시간 반환    
    let dateObj= new Date();

    let hours = dateObj.getHours();
    if(hours<10){
      hours='0'+hours;
    }
    let minutes = dateObj.getMinutes();
    if(minutes<10){
      minutes='0'+minutes;
    }
    let secs = dateObj.getSeconds();
    if(secs<10){
      secs='0'+secs;
    }

	// 주어진 형식에 맞게 문자열로 반환
    return template.replace('h',hours).replace('m',minutes).replace('s',secs);
  }

	// 1초마다 출력하기
  this.start = function(){
    setInterval(()=>console.log(getNow()),1000);
  };

}

let clock = new Clock({template:'h:m:s'});
clock.start();



클래스로 구현하기

class Clock{
	//생성자함수에서 해당하는 부분이다.
  constructor({template}){
    this.template = template;
  }

  // Clock.prototype에 정의된다.
  getNow(){
    let dateObj= new Date();

    let hours = dateObj.getHours();
    if(hours<10){
      hours='0'+hours;
    }
    let minutes = dateObj.getMinutes();
    if(minutes<10){
      minutes='0'+minutes;
    }
    let secs = dateObj.getSeconds();
    if(secs<10){
      secs='0'+secs;
    }

    return this.template.replace('h',hours).replace('m',minutes).replace('s',secs);
  }

  // 클래스필드로 정의하여 객체마다 정의된다.
  start = ()=>{
    setInterval(()=>console.log(this.getNow()),1000);
  };
}
let clock = new Clock({template:'h:m:s'});
clock.start();

start메소드를 클래스필드로 구현했는데 Clock.prototype에 정의하는것이 좋다.

start(){
    setInterval(()=>console.log(this.getNow()),1000);
  };

0개의 댓글