오늘한 일
- 코드 리뷰를 할 때 마다 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
위 에러를 통해 this 가 Rectangle 의 인스턴스가 아니라는 것을 확인할 수 있다.
위 this 는 window 객체를 바라보고 있다.
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 |