Javascript this Bind
javascript 에서 중요한 개념인 this에 대해 파헤쳐 보자.
The “this” keyword allows you to decide which object should be focal when invoking a function or a method.
this 키워드는 변수, 함수 등이 서로 다른 문맥에서 사용될 때 어떤 객체를 가리켜야하는지 결정해 준다.
Java의 this, Python의 self 와 같은 다른 객체지향 언어도 this 개념을 쓰지만 Javscript는 조금 다르게 동작한다.
Javascript는 함수가 호출될때 실행 컨텍스트(Execution Context)가 생성된다.
이 때, 함수가 어떻게 호출 되느냐에 따라 this가 바인딩 될 객체가 동적으로 결정된다.
5가지 바인딩 방법
1. new Binding
function Person() {
this.name = 'yhan';
this.say = function() {
console.log(this.name)
}
}
var yhan = new Person()
console.log(yhan.name) // => yhan
yhan.say() // => yhan
Javascript에서 함수는 객체 생성을 위해 사용될 수 있다. 함수가 new
키워드를 이용해 생성자 함수로서 호출될 때 다음과 같은 일이 일어난다.
- 빈 객체 생성 및 이 객체에 this 바인딩, prototype object 연결
- this 를 통한 프로퍼티, 메소드 생성
- 생성된 객체 반환
즉, this는 새롭게 생성된 인스턴스를 가리킨다.
2. 객체 메소드 (Implicit Binding)
var yhan = {
name: 'yhan',
say(){
console.log(this.name);
},
nest: {
name: 'kate',
say() {
console.log(this.name)
}
},
es6Say: () => {
console.log(this.name)
}
}
yhan.say() // => yhan
yhan.nest.say() // => kate
yhan.es6Say() // => undefined
함수가 어떤 객체의 메서드 형태로 호출되면 this는 그 객체를 가리킨다.
nest안의 say는 또 yhan이 아닌 nest의 메소드 임으로 this.name 은 kate이다.
arrow function에서 this는 렉시컬 바인딩이므로 window를 가리킨다.
3. call, apply, bind (Explicit Binding)
function say() {
console.log(this.name)
}
const yhan = {
name: 'yhan'
}
say.call(yhan) // => yhan
say.apply(yhan) // => yhan
(say.bind(yhan))() // => yhan
4. 일반/콜백/내부 함수 (Window Binding)
// 일반함수
var f = function() {
console.log(this)
}
f() // => window
// 내부함수
var obj = function() {
return function () {
console.log(this)
}
}
f()() // => window
var obj = {
f1() {
f2() {
console.log(this)
}
}
}
obj.f1() // => window
// 콜백함수
var obj = {
f() {
setTimeout(function() {
console.log(this)
}, 100)
}
}
obj.f() // => window
- 일반 함수, 내부 함수, 콜백 함수는 전역 객체에 바인딩 된다.
- 브라우저라면
window
객체, server-side(node) 에서는global
객체 이다. - strict mode 라면 undefined로 초기화 된다.
5. Arrow Function (Lexical Binding)
이 글에서 살펴본 것과 같이, arrow function은 렉시컬 스코프에 바인딩 된다.
var yhan = {
name: 'yhan',
say() {
var f = () => { console.log(this.name) }
f()
},
say2: () => {
var f2 = () => { console.log(this.name) }
f2()
}
}
yhan.say() // => yhan
yhan.say2() // => undefined
f, f2는 각각 say, say2가 참조하는 this를 참조한다.
say의 this는 yhan을 가리키지만 say2의 this는 window
객체를 가리킨다.
React class component method Binding
class Home extends React.Component {
update() {
this.setState({
newStuff: true
});
}
render() {
return (
<Button onClick={ this.update }>
button
</button>;
}
}
react에서 class component를 이용해 개발을 할때, 다음과 같은 코드에서 onClick 함수가 제대로 실행되지 않은 경우를 마주친 적이 있을 것이다.
function Person(name) {
this.name = name;
this.say = function(){
console.log(this.name);
}
}
var yhan = new Person('yhan');
yhan.say(); // yhan
var say = yhan.say;
say(); // => undefined
Javascript의 class는 function으로 이루어져 있기 때문에 위와 같이 작성하여 작동 원리를 알아볼 수 있다.
객체의 메서드로 say를 실행하면 원하는 결과가 나오지만, 다른 변수에 담는순간 메소드로서의 성질을 잃고 일반 함수가 된다.
위의 react 코드에서도 render 메소드가 update를 호출할 때 메소드를 호출 한것이 아닌 일반 함수로서 할당 한 것이므로 this를 잃어버리게 된다.
bind()
class Home extends React.Component {
constructor(props){
super(props);
this.update = this.update.bind(this);
}
update() {
this.setState({
newStuff: true
});
}
render() {
return (
<Button onClick={ this.update }>
button
</button>;
}
}
constructor 또는 render에서 bind 메서드를 사용하여 해결할 수 있다.
arrow function
class Home extends React.Component {
update = () => {
this.setState({
newStuff: true
});
}
render() {
return (
<Button onClick={ this.update }>
button
</button>;
}
}
함수 선언시 또는 render에서 호출 시 화살표 함수를 이용하여 문제를 해결할 수 있다.
Quiz
function F() {
this.a = 3;
this.p = function() {
console.log(this.a);
};
this.q = function() {
setTimeout(this.p, 1000)
};
}
function F1() {
this.a = 3;
this.p = function() {
console.log(this.a);
};
this.q = function() {
setTimeout(() => this.p(), 1000)
};
}
function F2() {
this.a = 3;
this.p = () => {
console.log(this.a);
};
this.q = function() {
setTimeout(this.p, 1000)
};
}
new F().q(); // 1
new F1().q(); // 2
new F2().q(); // 3
var obj = {
a: 3,
p() {
console.log(this.a);
},
q() {
setTimeout(this.p, 1000);
},
r() {
this.p();
},
};
var obj1 = {
a: 3,
p() {
console.log(this.a);
},
q() {
setTimeout(() => this.p(), 1000);
},
};
var obj2 = {
a: 3,
p: () => {
console.log(this.a);
},
q() {
setTimeout(this.p, 1000);
},
};
obj.q(); // 4
obj.r() // 5
obj1.q(); // 6
obj2.q(); // 7
퀴즈를 풀어 봅시다.
References
- MDN > this
- Must Know About Frontend > this의 바인딩
- PoiemaWeb > this
- U; > Understanding the ‘this’ keyword
- Hyeokwoo Alex Kwon > React에서의 바인딩 방법들
- This is why we need to bind event handlers in Class Components in React
quiz 정답
- undefined
- 3
- 3
- undefined
- 3
- 3
- undefined