본문 바로가기

Javascript/Javascript(ES6)

ES6 화살표 함수(arrow function)를 배우기 전 자바스크립트 this 이해하기

반응형

ES6 arrow function 배우기 전 자바스크립트 this 이해하기

ES6 arrow function을 배우기 전 이해를 돕기 위해 자바스크립트의 this 에 대해서 간단하게 정리한다.

JAVA같은 언어에서 this는 클래스로부터 생성되는 인스턴스 객체를 의미한다.

다른 의미를 가질 염려가 없어서 혼란이 생기지 않는다.

자바스크립트에서는 this는 함수의 현재 실행 문맥(context)이다.

자바 스크립트에서는 4가지의 함수 실행 타입이 존재한다.

1. 함수 실행 alert('hello world');

2. 메소드 실행 console.log('hello world');

3. 생성자 실행 new Sample();

4. 간접 실행 alert.call(undefined, 'hello world');

각 타입은 서로 다른 각각의 문맥을 가진다. (+strict모드에서는 또 다르다......)


1. 함수 실행에서의 this

1
2
3
4
function sum(a,b){
    console.log(this === window); //true
    return a+b;
}


일반적인 함수 실행에서 this는 window객체다.

1
2
3
4
5
function sum(a,b){
    'use strict'// strict모드
    console.log(this === undefined); //true
    return a+b;
}


strict모드로 실행되는 함수 실행에서 this는 undefined다.

* 실수 point

외부 함수에서의 this와 내부 함수에서의 this를 동일하게 생각하면 안된다.

내부 함수의 문맥은 오직 실행 환경에 따라 다르다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var numbers = {  
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     function calculate() {
       // this는 window, 엄격 모드였으면 undefined
       console.log(this === numbers); // => false
       return this.numberA + this.numberB;
     }
     return calculate();
   }
};
numbers.sum(); // NaN, 엄격 모드였으면 TypeError

내부의 calculate()는 외부 함수와 별개의 함수 실행으로 봐야 한다.

따라서 calculate()에서 this.numberA, this.numberB를 모르니까 계산할 수 없고 결과적으로 NaN이 나온다.

이 문제를 해결하려면 calculate()의 마지막에 return calculate.call(this); 라고 코딩해야 한다.

참고로 sum() 함수실행이 아니라 메소드 실행이다.


2. 메소드 실행에서의 this

this는 메소드 실행에서 메소드를 소유하고 있는 객체다.

1
2
3
4
5
6
7
8
var numbers = {  
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     return numberA+numberB; 
   }
};


아까 예에서 알 수 있듯이 객체(numbers)안의 메소드를 실행한다면 그 때 this는 객체 자신이다.

ES6의 문법으로 클래스(class)를 정의할 때도 메소드의 실행 문맥은 인스턴스 객체 자신이다.

1
2
3
4
5
6
7
8
9
10
11
12
class Planet {  
  constructor(name) {
    this.name = name;    
  }
  getName() {
    console.log(this === earth); // => true
    return this.name;
  }
}
var earth = new Planet('Earth');  
// 메소드 실행. 여기서의 this는 earth.
earth.getName(); // => 'Earth'


* 실수 point

객체 내에 있는 메소드는 별도의 변수로 분리가 가능하다.

이 변수를 통해 메소드를 호출할 때! this는 함수 호출에서의 문맥으로 바뀐다.

react 바인딩 관련 포스트에서도 다뤘듯 객체의 메서드를 다른 변수에 담을 수 있고

그 변수를 실행하는 방법으로 함수를 실행할 수 있지만 obj와의 관계가 상실되기 때문에 this가 달라진다.

1
2
3
4
5
6
7
8
9
10
11
12
function Animal(type, legs) {  
  this.type = type;
  this.legs = legs;  
  this.logInfo = function() {
    console.log(this === myCat); // => false
    console.log('The ' + this.type + ' has ' + this.legs + ' legs');
  }
}
var myCat = new Animal('Cat'4);  
// "The undefined has undefined legs" 출력
// 혹은 엄격모드라면 TypeError 출력
setTimeout(myCat.logInfo, 1000);


 여기서도 setTimeout의 매개변수로 전달되었기 때문에 객체로부터 관계가 상실되면서 this는 myCat이 아니게 된다.

해결 방법은 react에서도 그랬듯 setTimeout(myCat.logInfo.bind(myCat),1000); 이런식으로 bind()함수를 사용해야 한다.


3. 생성자 실행에서의 this

말만 다를 뿐이지 사실 메소드 실행에서의 this와 같다.

즉, 생성자 함수를 실행에서 새롭게 만들어진 객체가 실행문맥이다.

1
2
3
4
5
6
7
function Foo () {  
  console.log(this instanceof Foo); // => true
  this.property = 'Default Value';
}
// 생성자 실행
var fooInstance = new Foo();  
fooInstance.property; // => 'Default Value'


ES6에서의 class에서 생성자도 this는 새로 생긴 객체다.

1
2
3
4
5
6
7
8
9
class Bar {  
  constructor() {
    console.log(this instanceof Bar); // => true
    this.property = 'Default Value';
  }
}
// Constructor invocation
var barInstance = new Bar();  
barInstance.property; // => 'Default Value'


* 생성자 호출할 때 new를 빼먹는 실수만 하지 말자.


4. 간접 실행에서 this

간접 실행은 함수가 .call() 이나 .apply 메소드와 함께 호출될 때를 가리킨다.

.call(arg1,arg2,...) 메소드는 첫 번째 인자는 실행 문맥(this), 나머지 인자는 호출함수에 전달할 매개변수를 하나씩 받는다.

.aplly(arg1,[args]) 메소드는 첫 번째 인자는 실행 문맥(this), 나머지 인자는 호출 함수에 전달할 매개변수를 배열로 받는다.

1
2
3
4
5
6
7
8
var rabbit = { name'White Rabbit' };  
function concatName(string) {  
  console.log(this === rabbit); // => true
  return string + this.name;
}
// Indirect invocations
concatName.call(rabbit, 'Hello ');  // => 'Hello White Rabbit'  
concatName.apply(rabbit, ['Bye ']); // => 'Bye White Rabbit'


간접 실행은 함수의 실행 문맥을 지정할 수 있기 때문에 유용하다.

위에서 함수 실행에서 this는 항상 window, strict모드에서는 undefined를 가리키는데 이 문제를 간접 실행으로 해결할 수 있다.

결국 간접 실행에서 this는 넘겨주기용 같다.(아직 명확하게 이해못했음.)


참고 사이트

https://github.com/FEDevelopers/tech.description/wiki/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-this%EC%97%90-%EB%8C%80%ED%95%9C-%EC%84%A4%EB%AA%85-1#1-this%EC%97%90-%EB%8C%80%ED%95%9C-%EB%AF%B8%EC%8A%A4%ED%84%B0%EB%A6%AC

반응형