Voyage_dev
항해하는 개발자
Voyage_dev
전체 방문자
오늘
어제
  • All (158)
    • Front-End (102)
      • HTML & CSS (7)
      • JavaScript (13)
      • Web & 표준 & ETC (15)
      • React (17)
      • Next (13)
      • TypeScript (6)
      • Zustand (1)
      • AWS (4)
      • Redux (1)
      • MobX (9)
      • GraphQL (1)
      • Flutter (15)
    • 알고리즘 (12)
      • Java (11)
      • JavaScript (1)
    • Git (1)
    • Project (2)
      • 개인 (1)
      • 팀 (1)
    • Books (30)
      • 누구나 자료구조와 알고리즘 (20)
      • Do It 타입스크립트 프로그래밍 (9)
    • 자료구조 (6)
    • 취업 (1)
    • 항해플러스 (1)
      • 1주 (1)

블로그 메뉴

  • 👨🏻‍💻 Github
  • 💻 Rss 블로그
  • 🏊‍♂️ Resume
  • 🗂️ Portfolio

인기 글

최근 댓글

hELLO · Designed By 정상우.
Voyage_dev

항해하는 개발자

Front-End/JavaScript

[JavaScript] this란?

2022. 3. 13. 16:04

this란?

  • this란 ‘이것' 이란 뜻이다
  • 자바스크립트 내에서 this는 ‘누가 나를 불렀느냐'를 뜻한다
  • 자바스크립트 예약어
  • 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가르키는 자기 참조 변수 (self-reference variable)이다
  • 자바스크립트 엔진에 의해 암묵적으로 생성된다
  • this는 코드 어디서든 참조할 수 있다
  • 하지만 this는 객체의 프로퍼티나 메서드를 참조하기 위한 자기 변수이므로 일반적으로 객체의 메서드 내부 또는 생성자 함수 내부에서만 의미가 있다
  • 대부분의 경우, this 바인딩의 값은 함수를 호출하는 방법에 의해 결정된다

JavaScript에서 함수의 this 키워드는 다른 언어들과 비교하여 조금 다르게 동작한다. 또한 strict mode와 non-strict mode 사이에서도 조금 다르다

this의 이해 (바인딩)

this의 값은 어떻게 변화할까? this가 어떤 값과 연결되는 지는 this의 바인딩을 통해서 확인해 볼 수 있다.

바인딩이란, this의 호출 방식에 따라서 this가 특정 객체에 연결되는 것이다.

  • 식별자와 값을 연결하는 과정을 말한다
  • 변수선언은 변수 이름과 확보된 메모리 공간의 주소를 바인딩하는 것
  • 즉, this 바인딩은 this와 this가 가르킬 객체를 바인딩하는 것

this의 바인딩은 일반 함수 내부, 메서드 내부, 생성자 함수 내부, Call, Apply, Bind를 통한 ‘호출 방식'으로 나눠서 볼 수 있다

  1. 일반 함수 내부에서의 this는 글로벌 객체와 바인딩된다.
  2. 메서드 내부에서의 this는 메서드를 호출한 객체와 바인딩된다.
  3. 생성자 함수 내부에서 this는 생성자 함수가 생헐할 인스턴스와 바인딩된다.
  4. Call, Apply, Bind 메서드 사용 시, 메서드에 첫 번째 인수로 전달하는 객체에 바인딩 된다.

this를 전역에서 단독으로 사용한 경우

  • 브라우저라는 자바스크립트 런타임의 경우에 this는 항상 window라는 전역 객체를 참조한다
  • 전역 객체란 전역 범위에 항상 존재하는 객체
  • 모든 변수, 함수는 window라는 객체의 프로퍼티와 메소드이다
  • 이는 ES5에서 추가된 strict mode (엄격 모드) 에서도 마찬가지이다
'use strict';
 
var x = this;
console.log(x); //Window

this를 함수 내부에서 사용한 경우

  • 함수 안에서 this는 함수의 주인에게 바인딩된다
  • 함수의 주인은? = window
  • 즉, 모든 함수는 객체 내부에 있으며 this는 현재 함수를 실행하고 있는 그 객체를 참조한다
function myFunction() {
  return this;
}
console.log(myFunction()); //Window
var num = 0;
function addNum() {
  this.num = 100;
  num++;
  
  console.log(num); // 101
  console.log(window.num); // 101
  console.log(num === window.num); // true
}
 
addNum();

위 예시에서 this.num의 this는 window 객체를 가르키므로 num은 전역 변수를 가리킨다

  • strict mode (엄격모드)에서는 조금 다르다
  • 함수 내의 this에 default 바인딩이 없기 때문에 undefined
"use strict";
 
function myFunction() {
  return this;
}
console.log(myFunction()); //undefined
"use strict";
 
var num = 0;
function addNum() {
  this.num = 100; //ERROR! Cannot set property 'num' of undefined
  num++;
}
 
addNum();

여기서 this.num을 호출하면 undefined.num을 호출하는 것과 마찬가지 = 에러

메서드 안에서 쓴 this

  • 메서드 호출 시 메서드 내부 코드에서 사용된 this는 해당 메서드를 호출한 객체로 바인딩된다
var person = {
  firstName: 'John',
  lastName: 'Doe',
  fullName: function () {
    return this.firstName + ' ' + this.lastName;
  },
};
 
person.fullName(); //"John Doe"

이벤트 핸들러 안에서 쓴 this

  • 이벤트 핸들러에서 this는 이벤트를 받는 HTML 요소를 가르킨다
var btn = document.querySelector('#btn')
btn.addEventListener('click', function () {
  console.log(this); //#btn
});

생성자 안에서 쓴 this (new 바인딩)

  • 생성자 함수가 생성하는 객체로 this가 바인딩 된다
function Person(name) {
  this.name = name;
}
 
var kim = new Person('choi');
var lee = new Person('lee');
 
console.log(kim.name); //choi
console.log(lee.name); //lee
  • 하지만 new 키워드를 빼먹는 순간 일반 함수 호출과 같아지기 때문에, window에 바인딩된다
var name = 'window';
function Person(name) {
  this.name = name;
}
 
var kim = Person('kim');
 
console.log(window.name); //kim

명시적 바인딩을 한 this

  • apply() / call() 메서드는 Function Object에 기본적으로 정의된 메서드
  • 인자를 this로 만들어주는 기능
function whoisThis() {
  console.log(this);
}
 
whoisThis(); //window
 
var obj = {
  x: 123,
};
 
whoisThis.call(obj); //{x:123}
  • apply()에서 매개변수로 받은 첫 번째 값은 함수 내부에서 사용되는 this에 반영, 두 번째 값인 배열은 자신을 호출한 함수의 인자로 사용
function Character(name, level) {
  this.name = name;
  this.level = level;
}
 
function Player(name, level, job) {
  this.name = name;
  this.level = level;
  this.job = job;
}

이렇게 두 생성자 함수가 있을 때, this.name과 this.level을 받아오는 부분이 똑같으니 이럴 때 apply()을 쓸 수 있다

function Character(name, level) {
  this.name = name;
  this.level = level;
}
 
function Player(name, level, job) {
  Character.apply(this, [name, level]);
  this.job = job;
}
 
var me = new Player('Nana', 10, 'Magician');
  • call()은 인수 목록을 받고 apply()는 인수 배열을 받는다는 차이가 있다
  • 위 코드를 call()로 바꾼다면
function Character(name, level) {
  this.name = name;
  this.level = level;
}
 
function Player(name, level, job) {
  Character.call(this, name, level);
  this.job = job;
}
 
var me = {};
Player.call(me, 'nana', 10, 'Magician');
  • apply() 혹은 call()은 보통 유사배열 객체에게 배열 메서드를 쓰고자 할 때 사용
  • 예를들어, arguments 객체는 함수에 전달된 인수를 Array 형태로 보여주지만 배열 메서드를 쓸 수가 없다. 이럴 때 가져다 쓸 수 있다
function func(a, b, c) {
  console.log(arguments);
 
  arguments.push('hi!'); //ERROR! (arguments.push is not a function);
}
function func(a, b, c) {
  var args = Array.prototype.slice.apply(arguments);
  args.push('hi!');
  console.dir(args);
}
 
func(1, 2, 3); // [ 1, 2, 3, 'hi!' ]
var list = {
  0: 'Kim',
  1: 'Lee',
  2: 'Park',
  length: 3,
};
 
Array.prototype.push.call(list, 'Choi');
console.log(list);
  • 추가로 ES6부터는 Array.from()이라는 메서드를 쓸 수 있다
  • 유사배열객체를 얕게 복사해 새 Array 객체로 만든다
var children = document.body.children; // HTMLCollection
 
children.forEach(function (el) {
  el.classList.add('on'); //ERROR! (children.forEach is not a function)
});
var children = document.body.children; // HTMLCollection
 
Array.from(children).forEach(function (el) {
  el.classList.add('on'); 
});

화살표 함수로 쓴 this

  • 화살표 함수는 전역 컨텍스트에서 실행되더라도 this를 새로 정의하지 않고, 바로 바깥 함수나 클래스의 this를 쓴다
  • 화살표 함수 안에서 thiss는 언제나 상위 스코프의 this를 가리킨다
  • 화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다
  • 화살표 함수의 this 바인딩 객체 결정 방식은 함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프와 유사하다
  • 화살표 함수는 call, apply, bind 메서드를 사용하여 this를 변경할 수 없다
var Person = function (name, age) {
  this.name = name;
  this.age = age;
  this.say = function () {
    console.log(this); // Person {name: "Nana", age: 28}
 
    setTimeout(function () {
      console.log(this); // Window
      console.log(this.name + ' is ' + this.age + ' years old');
    }, 100);
  };
};
var me = new Person('Nana', 28);
 
me.say(); //global is undefined years old
  • 위 코드를 보면 내부 함수에서 this가 전역 객체를 가리키는 바람에 의도와는 다른 결과가 나왔다
var Person = function (name, age) {
  this.name = name;
  this.age = age;
  this.say = function () {
    console.log(this); // Person {name: "Nana", age: 28}
 
    setTimeout(() => {
      console.log(this); // Person {name: "Nana", age: 28}
      console.log(this.name + ' is ' + this.age + ' years old'); 
    }, 100);
  };
};
var me = new Person('Nana', 28); //Nana is 28 years old
  • 화살표 함수로 바꾸면 제대로 된 결과가 나온다

결론

  • this는 어떤 위치에 있고, 어디에서 호출되었고, 어떤 함수에 있느냐에 따라 참조 값이 달라지는 특성이 있다
  • this가 바로 호출할 때 결정되는 것, 그 this는 호출한 객체가 바로 this라는 것
  • 그리고 그 this를 호출과 무관하게 고정시켜 줄 수 있는 아이가 바로 bind 메서드
  • bind라는 함수를 사용해서 묶어주면 this는 호출과 무관하게 고정된다
  • 이외에도 call, apply 메서드 또한 같은 역할

출처 : 아래의 사이트들을 보면서 큰 공부 하였습니다

https://hanamon.kr/javascript-this란-무엇일까/

https://yuddomack.tistory.com/entry/자바스크립트-this의-4가지-동작-방식

https://nykim.work/71

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this

https://velog.io/@realryankim/JavaScript-this란

https://ljtaek2.tistory.com/153?category=863722

모던 자바스크립트 - this

'Front-End > JavaScript' 카테고리의 다른 글

프로토타입 (Prototype)과 객체지향 프로그래밍  (0) 2024.03.25
[JavaScript] Map Object (맵)  (0) 2022.04.03
[JavaScript] Closure (클로저)  (0) 2022.03.09
[JavaScript] var / let / const 변수  (0) 2022.03.09
[JavaScript] 스코프 (Scope)  (0) 2022.03.07
    'Front-End/JavaScript' 카테고리의 다른 글
    • 프로토타입 (Prototype)과 객체지향 프로그래밍
    • [JavaScript] Map Object (맵)
    • [JavaScript] Closure (클로저)
    • [JavaScript] var / let / const 변수
    Voyage_dev
    Voyage_dev

    티스토리툴바