본문 바로가기

오늘 한 일을 기록하자/TIL

210128_TIL... bind()

오늘한 일

  • 코드 리뷰를 할 때 마다 this, call, apply, bind 만 나오면 멈칫멈칫 했다. 그래서 오늘 확실히 개념을 잡고 싶어 다시 사용법, 예제를 보면서 공부했다.

 

 

오늘 느낀점

 

bind() 메소드가 호출되면 새로운 함수를 생성합니다. 받게되는 첫 인자의 value 로는 this 키워드를 설정하고, 이어지는 인자들은 바인드된 함수의 인수에 제공됩니다.

 

bind() call() 과 유사하게 this 및 인자를 바인딩하나, 당장 실행하는 것이 아닌 바인딩된 함수를 리턴하는 함수이다.

 

 

case 1 : 이벤트 핸들러

 

bind 는 이벤트 핸들러에서 이벤트 객체 대신 다른 값을 전달하고자 할 때 유용하다.

 

<button id='btn'>클릭하세요</button>
let btn = document.querySelector('#btn')
btn.onclick = handleClick

function handleClick() {
  console.log(this)
}

 

 

콘솔창을 확인해 보면

handleClick 함수에서 확인하는 this 값은 button 엘리먼트 자체이다.

 

이때 bind 를 써서 this 를 변경해보자.

let btn = document.querySelector('#btn')
btn.onclick = handleClick.bind({hello : 'world', bye : 'good'}) // bind 를 추가해봤다

function handleClick() {
  console.log(this)
  console.log(this.hello)
  console.log(this.bye)
}

 

 

 

 

 

case 2 : 동적인 버튼의 이벤트

 

동적으로 여러 개의 버튼을 만들고, 각각의 이벤트 핸들러에 각기 다른 값을 바인딩해야 할 경우를 생각해보자.

각 버튼을 클릭할 때, 이름이 콘솔로 표시되게 만들어 본다.

 

<div id='target'></div>
let target = document.querySelector('#target')
let users = ['유재석','박명수','정준하','하하','정형돈','노홍철']

users.forEach(function(user) {
  let btn = document.createElement('button')
  btn.textContent = user
  btn.onclick = handleClick
  target.appendChild(btn)
});

function handleClick() {
  console.log(this)
}

 

 

왼쪽이 콘솔창 <-> 오른쪽은 output

forEach 로 동적으로 생성되는 각각의 버튼을 클릭하면 button 엘리먼트 자체가 콘솔에 표시되고 있다.

이 때 bind 를 이용해 출력하고 싶은 값을 this 로 넘기거나, 혹은 인자로 보낼 수 있다.

 

 

 

Solution 1

let target = document.querySelector('#target')
let users = ['유재석','박명수','정준하','하하','정형돈','노홍철']

users.forEach(function(user) {
  let btn = document.createElement('button')
  btn.textContent = user
  btn.onclick = handleClick.bind(user) // bind 를 이용해 user 를 this 로 넘김
  target.appendChild(btn)
});

function handleClick() {
  console.log(this)
}

 

 

 

Solution 2

 

let target = document.querySelector('#target')
let users = ['유재석','박명수','정준하','하하','정형돈','노홍철']

users.forEach(function(user) {
  let btn = document.createElement('button')
  btn.textContent = user
  btn.onclick = handleClick.bind(null, user) // null 을 이용해 봤다
  target.appendChild(btn)
});

function handleClick(user) {
  console.log(user)
}

 

굳이 this 를 이용하지 않더라도 인자로 넘길 수도 있다.

 

 

Solution 3

 

let target = document.querySelector('#target')
let users = ['유재석','박명수','정준하','하하','정형돈','노홍철']

users.forEach(function(user) {
  let btn = document.createElement('button')
  btn.textContent = user
  btn.onclick = function() {  // 익명함수를 이용
    handleClick(user)
  }
  target.appendChild(btn)
});

function handleClick(user) {
  console.log(user)
}

 

bind 를 사용하지 않고 익명 함수로도 가능하다.

 

 

 

case 2 : setTimeout

 

setTimeout() 은 시간 지연을 일으킨 후 함수를 비동기적으로 실행하게 하는 함수이다.

window 객체를 this 에 바인딩하는 특징이 있다.

 

class Rectangle {
  constructor(width, height) {
    this.width = width
    this.height = height
  }
  
  // 사각형의 넓이를 구한다
  getArea() {
    return this.width * this.height
  }
  
  // 사각형의 넓이를 계산한 값을 콘솔에 표시해준다
  printArea() {
    console.log('사각형의 넓이는 ' + this.getArea() + ' 입니다.')
  }
  
  // 즉시 사각형의 넓이를 콘솔에 표시한다.
  printSync() {
    this.printArea()
  }
  
  printAsync() {
    setTimeout(this.printArea, 2000)
  }
  
}

let box = new Rectangle(40,20)
box.printSync()   // '사각형의 넓이는 800 입니다'
box.printAsync()  // Error

 

위 에러를 통해 thisRectangle 의 인스턴스가 아니라는 것을 확인할 수 있다.

thiswindow 객체를 바라보고 있다.

window 객체에 getArea() 는 당연히 존재하지 않으므로 실행을 못하고 에러를 뱉는다.

 

 

 

Solution 1

 

printAsync() 에서 bind 를 이용해 수정해보자.

  printAsync() {
    setTimeout(this.printArea.bind(this), 2000)
  }

 

 

정상적으로 2초 뒤에 실행되 콘솔창에 표시되는 모습을 확인할 수 있었다.

'오늘 한 일을 기록하자 > TIL' 카테고리의 다른 글

210303_TIL... MVC Design Patterns  (0) 2021.03.03
210223_TIL... Redux  (0) 2021.02.22
210119_TIL.. 자료구조(Stack, Queue)  (0) 2021.01.19
210114_TIL.. OOP에 대하여  (0) 2021.01.14
210113_TIL  (0) 2021.01.13