2020. 11. 1. 19:37ㆍTech Article/Javascript
this와 call(), apply(), bind()
this는 javascript의 이해하기 어려운 내용 중 하나입니다. 저 또한 this를 이해하지 못하였을 때, this에 대한 두려움이 많았습니다.
하지만 단 한번 제대로 이해를 하고 나니 오히려 js가 더 재밌게 느껴졌습니다.
this에 대해 이해를 아직 잘모르신다면 반드시 짚고 넘어가도록 권하고 싶습니다.
Content
크게 아래와 같은 내용으로 나눠서 설명하였습니다.
call()
에 대한 이해apply()
에 대한 이해bind()
에 대한 이해- function에서의
this
- class에서의
this
Description
this란 MDN에서 이렇게 설명한다.
the value of
this
is determined by how a function is called (runtime binding)
function에서 사용되는 value임을 알 수 있다. this는 execution context(global, function 등)의 property이다.
default로는 global을 가리키고 있다.
console.log(this)
를 해보면 console.log(window)
와 같음을 알 수 있다.
=> Chrome의 developer tool의 console에 연습해보도록 하자.
this의 특징은 lexical scope를 갖지 않는 다는 것이다. 이것은 우리의 예측을 빗나가게 만든다.
직접적으로 어떤context인지 지정하지 않으면 항상 global을 가리킨다.
1. call()
에 대한 이해
어디에 쓰이는가?
여러 object들이 하나의 method(function)들을 공유할 수 있다. 즉 code를 clean하고 dry하게 작성할 수 있다.
우리는 function을 invoke할때와 같이 한다. 이 때foo()
은 foo.call()
과 같은 것이다.
function foo() {
console.log("foo yes");
}
foo();
foo()
의 parenthesis안에는 argument들을 넣는데, call은 첫번째 자리에 object가 들어가게 된다. this로 연결시킬 object를 정해주는 것이다.
따라서 아래와 같이 표현될 수 있다.
function foo(age, gender) {
console.log(`name: ${this.name}, age: ${age}, gender: ${gender}`);
}
let obj = { name: "cho" };
foo.call(obj, 20, "female"); //name: cho, age: 20, gender: female
foo(30, "male"); //name: , age:30, gender: male
line number | 이유 |
---|---|
line 7 | call로 this로 연결할 object를 지정해줌, 따라서 name에 cho가 출력 됨 |
line 8 | this로 연결될 object가 지정되어 있지 않음, 따라서 undefined로 아무것도 나오지 않음 |
2. apply()
에 대한 이해
call과 차이점이 무엇인가?
apply와 call의 기능은 같다. 단지 syntax의 차이가 있을 뿐이다.
call 사용시 function의 arguments를 하나씩 넣어줘야한다.
function foo(age, gender) {
console.log(`name: ${this.name}, age: ${age}, gender: ${gender}`);
}
let obj = { name: "cho" };
foo.call(obj, 20, "female"); //name: cho, age: 20, gender: female
apply는 function의 arguments를 array로 넣어준다.
function foo(age, gender) {
console.log(`name: ${this.name}, age: ${age}, gender: ${gender}`);
}
let obj = { name: "cho" };
let objArgs = [20, "female"];
foo.apply(obj, objArgs); //name: cho, age: 20, gender: female
3. bind()
에 대한 이해
bind는 왜 쓰는가?
간혹 function을 그 자리에서 실행하지 않고, variable에 할당하고 저장해두는 경우가 있습니다. 즉 저장의 용도로 사용하는 것입니다.
call과 apply는 그 자리에서 function을 invoke(IIFE, Immediately Invoke Function Expression)하는 반면, bind는 function을 저장하여 나중에 invoke할 수 있습니다.
function foo(age, gender) {
console.log(`name: ${this.name}, age: ${age}, gender: ${gender}`);
}
let obj = { name: "cho" };
let fooBindO = foo.bind(obj, 20, "female");
let fooBindX = foo;
fooBindX(); //name: , age: 20, gender: female
fooBindO(); //name: cho, age: 20, gender: female
line number | 이유 |
---|---|
line 9 | bind하지 않고, function을 저장한 경우 |
line 10 | object를 bind하고, function을 저장한 경우 |
fooBindX는 bind가 되지 않았을 경우이고, fooBindO는 bind를 했을 경우입니다.
주석에서 각각의 결과값을 보면, fooBindX는 name 부분이 undefined임을 알 수 있습니다.(비어있음)
따라서 fooBindX와 같이 bind없이 함수만 assign하면 this를 잃어버립니다. 따라서 fooBindX의 형태로 쓰지 않도록 합니다.
4. function context 에서의 this
아래 코드에 실행순서는 주석의 #1.부터 #5.까지 따라가면 됩니다.
function a() {
//#2. this for function a
console.log("function a: ", this); //global this
//#3. this for function b
function b() {
console.log("function b: ", this); //global this
}
b();
//#4. this for object and assign to another variable then invoke it
let bBindX = {
say: "oh no",
b: function () {
console.log("function b: ", this); //global this
console.log("function b say: ", this.say); //undefined
},
};
let saveB = bBindX.b;
saveB();
//#5. this for object and IIFE
let bBindO = {
say: "oh yes",
b: function () {
console.log("function b binding: ", this); //function b this
console.log("function b binding say: ", this.say); //oh yes
},
};
bBindO.b();
}
//#1. invoke function a
a();
위 코드에 대해서 console 출력의 이유에 대해서 말해보면
line number | 이유 |
---|---|
line 3 | this는 lexical하지 않기 때문에, global execution context를 갖게 됨 |
line 6 | this는 lexical하지 않기 때문에, function a를 무시하고 global execution context를 갖게 됨 |
line 13 | function을 variable에 assign하면 reference를 잃게 됨, 따라서 lexical하지 못하므로, global을 갖게됨 |
line 14 | function을 variable에 assign하면 reference를 잃게 됨, 따라서 undefined가 나타남 |
line 23 | function을 object(bBindO)로 부터 직접 call했으므로, reference를 잃지않음, 따라서 this가 object에 binding됨 |
line 17에서 variable에 할당하면서 reference를 잃게 되는 것이 포인트이다.
이를 다시 bind시켜주려면, line17에서 bBindX.b.bind(bBindX)
로 항상 bind되도록 해주면서 variable에 assign해야한다.
bind라는 것은 object와 function을 항상 연결시켜주는 것을 말한다.
5. class context 에서의 this
MDN을 보면 이렇게 나와있다.
Within a class constructor, this is a regular object. All non-static methods within the class are added to the prototypes of this
class에서 this는 보통 object와 같으며, static이 아닌 class내의 method들은 this에 prototype으로 추가된다.
아래 예제를 보면 line 의 출력은 myApp{name: "cho"}
이다. 즉, this는 class의 non-static에 대한 정보들을 담고 있으며, class execution context 환경에 bind되게 된다.
이는 우리가 React의 class타입에서 자주보는 this.handleClick = this.handleClick.bind(this);
와 관련이 있다. [CodePen 코드 보기]
bind(object)가 되게 되는데, class에서는 this가 regular object이기 때문에, 이렇게 쓰이는 것이다.
class myApp {
constructor() {
this.name = "cho";
}
myAppfunc() {
console.log("this: ", this);
}
}
const myAppInstance = new myApp();
myAppInstance.myAppfunc();
Example
Reference Link
'Tech Article > Javascript' 카테고리의 다른 글
[js] Truthy in Boolean (0) | 2021.04.22 |
---|---|
[Redux] Redux의 간단한 사용법 (codepen 예제 포함) (0) | 2021.04.18 |
[Javascript DOM] DOM Manipulation(DOM 조작하기) (0) | 2020.10.29 |
[Javascript DOM] The DOM (Document Object Model)이란? (0) | 2020.10.25 |
Execution context (0) | 2020.10.22 |