자바스크립트의 This

박제구·2021년 7월 30일
0

JavaScript

목록 보기
5/6
post-thumbnail

🧩 자바스크립트에서 This

This

자바스크립트에서 This는 함수의 호출 방식에 따라 객체가 동적으로 결정된다.


전역객체 This

  • 웹 브라우저에서 전역 This 는 window 객체를 의미
function thisBrowser() {
  console.log(this === window);
}

thisBrowser(); // true
  • NodeJS 환경에서는 전역 This 는 global 객체를 의미
function thisNodeJs() {
  console.log(this === global);
}

thisNodeJs(); // true 

전역객체와 let, var

  • 변수선언을 통해 전역객체(This)의 속성을 정의해보기
var money = 100

console.log(this.money) // 100

let cash = 200

console.log(this.cash) // undefined

var 변수는 전역객체의 속성을 정의할 수 있지만, let 변수는 전역객체의 속성을 정의할 수 없다.

🧩 1 일반 함수

자바스크립트에서 일반적인 함수의 호출시 This 는 전역객체(window or global)로 바인딩 된다.

var money = 100

function test(){
   console.log(this.money) // 100
   var money = 200
   function innerTest(){
      console.log(this.money) // 100
   }
   innerTest()
}

test()

test 함수의 경우 this가 전역을 가리키는 것으로 100이라는 값이 당연하다.
하지만, innerTest 함수인 함수의 내부함수 조차도
this는 전역객체(window 혹은 global)로 바인딩 되는 것을 알 수 있다.

var money = 100;

function test(){
  var money = 200;
  printMoney()
}

function printMoney(){
  var money = 300;
  console.log(this.money) // 100
}

test() 

위와 같이 일반 함수를 외부에서 선언하고 test 함수에서 호출해도 결국 전역객체가 바인딩된다.

🧩 2 메소드 함수

함수가 객체의 프로퍼티로 선언되고 메소드로서 호출되면, this는 메소드의 소유 객체로 바인딩된다.

var money = 100;

const obj = {
   money: 200,
   test(){
      console.log(this.money) // 200
   }
}

console.log(this.money) // 100
obj.test()

위의 예시에서 obj 객체에 test는 메소드로서 호출 위치가 obj 객체이기 때문에 this는 더이상 전역객체가 아니다.

var money = 100;

function printMoney(){
  console.log(this.money);
}

const obj1 = {
  money: 200,
  test: printMoney
}

const obj2 = {
  money: 300,
  test: printMoney
}

printMoney(); // 100
obj1.test(); // 200
obj2.test(); // 300

위에서는 printMoney 함수를 선언하고 각 오브젝트에 메소드로 넘겼다.
obj1의 메소드 호출시 this는 obj1,
obj2의 메소드 호출시 this는 obj2 라는 것을 알 수 있고,
결국 함수의 선언과는 상관없이 호출되는 곳이 this 값을 결정하는 것을 명확히 알 수 있다.

var money = 100;
const obj1 = {
  money: 200,
  printMoney(){
    console.log(this.money)
 }
}

const obj2 = {
  money: 300,
  printMoney: obj1.printMoney
}

obj1.printMoney() // 200, this -> obj1 
obj2.printMoney() // 300, this -> obj2 

조금더 복잡한? 예시를 확인할 수 있다.

🧩 3 명시적 바인딩

arguments ?

명시적 바인딩 전에 먼저 arguments에 대해 짚고 넘어간다.

함수 선언식으로 생성된 모든 함수는 arguments 객체를 갖는다.
함수 실행컨텍스트 생성시 LexicalEnvironment 컴포넌트안의 EnvironmentRecord를 확인하면, 
Arguments 객체를 받고, 0번부터 시작하는 index: value 로 매핑 되어있고 length 또한 속성으로 갖고 있다.
ex) Arguments: {0: 100, 1: 200, length: 2}     

함수안에서 arguments를 호출하여 인자로 들어온 값들을 확인 할 수 있다.
뒤에 정의하게될 This 객체를 명시적으로 바인딩하는 apply와 call함수는 arguments를 받는 부분에서만 차이가 있다.

This를 apply, call, bind 메소드를 통해 원하는 객체로 바인딩하여 실행할 수 있다.

  • apply
var money = 100;
function test () {
  console.log(this.money) // 999
  console.log(arguments)  // {0:200, 1:300}
}

const obj = {
  money: 999
}

test.apply(obj, [200, 300])
// 첫번째 인자가 this 바인딩 되고, 두번째 인자(배열)이 arguments로 들어감

apply 메소드는 첫번째 인자로 함수의 this를 바인딩하고 두번째 인자를 배열로 받아 arguments를 전달하고 실행한다.

  • call
var money = 100;
function test () { 
  console.log(this.money) // 999
  console.log(arguments)  // {0:200, 1:300}
}

const obj = {
  money: 999
}

test.call(obj, 200, 300) 
// 첫번째 인자가 this 바인딩 되고, 나머지가 arguments 로 순서대로 들어감

call 메소드는 첫번째 인자로 함수의 this를 바인딩하고 두번째 인자부터 순서대로 arguments에 전달후 실행한다. (apply 와의 차이점은 arguments를 받는 부분)

  • bind
var money = 100;
function test () {
  console.log(this.money) // 999
  console.log(arguments)  // {0:200, 1:300}
}

const obj = {
  money: 999
}

test.bind(obj)(200, 300)

bind 메소드의 경우 apply, call 과 비슷하게 동작하지만 함수를 실행시키진 않기 때문에 직접 실행해 주어야 한다.

🧩 4 New 키워드

new 키워드를 사용한 생성자 함수는 this 바인딩이 전역 객체를 가리키지 않는다.

function Money(){
  console.log(this === window) // false
}

const test = new Money(); 
console.log(test) // {} -> this는 Money라는 빈 객체

위의 예시에서console.log(test)를 한 결과가 빈객체로 나오는 것을 알 수 있는데
이는 New 키워드로 생성한 함수가 다음과 같은 절차를 따르기 때문이다.

New 연산자로 생성한 함수의 특징
1. This를 새로운 빈객체로 생성
2. This에 속성을 할당할 수 있음
3. 리턴값이 없으면 This를 자동 리턴

결국 새로운 this={}를 만들어 내고, 속성을 정의할 수 있으며 return 까지 해준다는 것!

function Olympic (country, medal){
  this.country = country;
  this.medal = medal;
}

const test1 = new Olympic('korea', 'gold');
const test2 = new Olympic('japan', 'silver');

console.log(test1) // {country: 'korea', medal: 'gold'}
console.log(test2) // {country: 'japan', medal: 'silver'}

🧩 Arrow function 과 This

Arrow function 에서 this는 상위스코프의 this를 가리킨다.

const obj = {
   nomal(){
      console.log(this === window) // false
   },
   arrow: () => {
      console.log(this === window) // true
   }
}

obj.nomal()
obj.arrow()

obj 라는 객체를 통해 호출한 두 함수에서 차이를 알 수 있는데,
nomal 함수의 this는 obj의 메소드로 호출한 일반 함수라서 obj가 this를 의미한다.
반면 arrow 함수는 obj의 메소드로 호출하였지만,
arrow function 이므로 함수가 정의된 obj객체의 상위스코프,
즉 global 객체를 참조하게 되고, window 가 this로 바인딩 된다.

var money = 999
const obj = {
   money: 100,
   nomal(){
      console.log(this.money) // 100
   },
   arrow: () => {
      console.log(this.money) // 999
   }
}
obj.nomal()
obj.arrow()

위 예시를 통해 일반함수와 Arrow함수 사이의
명확히 바인딩 되는 this에 대해 확인할 수 있다.

위 예시를 조금더 확장해 보면,

var money = 999
const obj = {
   money: 100,
   nomal(){
      console.log(this.money) 
   },
   arrow: () => {
      console.log(this.money)
   },
   inner(){
      const innerArrow = () => {
         console.log(this.money)
         console.log(this === obj) // true
      }
      return innerArrow
   }
}

obj.nomal() // 100
obj.arrow() // 999
obj.inner()() // 100

새로 추가된 inner 함수의 innerArrow 함수를 정의 했는데,
위 arrow function의 경우 this가 window를 바인딩 했지만
innerArrow 의 경우 상위스코프인 inner 함수를 참조하여 this는 obj 를 가리킨다.

arrow function의 arguments

const arrow = () => {
   console.log(arguments)
}
arrow() // error

function nomal(){
   console.log(arguments)
}
nomal() // {}

arrow 함수는 arguments 속성을 갖고 있지 않다.

References


poiemaweb

profile
안녕하세요!

0개의 댓글