Javascript this

Seokki Oh·2022년 6월 11일
0

js공부는 계획에 없었는데 오랜만에 다시 돌아볼 겸 기본적인 내용을 다시 공부해보았다

this

자바스크립트의 this는 다른 언어의 this와 다른 점이 많다고 한다
예전에 java언어를 잠깐 배웠을 때 단순하고 직관적으로 this를 딱히 공부할 것도 없이 직관적으로
바인딩했던 것 같은 기억이 있는데 확실히 js에서는 조금 다른 느낌이다
this를 사용하는 시점에서 자기가 속해 있는 scope에 접근하길 기대하는 점에서 같다고 볼 수 있을 것 같다
아래와 같이 말이다

const obj = {
  name: 'osk',
  sayName() {
    console.log(this.name);
  }
}
obj.sayName(); // 'osk'

기본적으로 this가 가리키는 곳

기본적으로는 this는 window(global)을 가리킴을 기억하자
window로 바인딩되지 않는 몇몇 경우가 있다 아래도 그 중의 하나다

const obj = {
  name: 'osk',
  outter() {
  	console.log(this.name);
	function inner() {
      console.log(this.name);
    }
    inner();
  }
}

obj.outter();
// osk
// '' chrome개발자도구 콘솔에서 window.name이 ''(빈값)으로 되어 있다 보통 undefined다

위처럼 객체의 메소드로 사용될 경우엔 사용된 객체에 바인딩이 된다
하지만 메소드로 사용되지 않은 단순한 안쪽의 함수로 호출된 경우엔 기본 바인딩에 따라 window에 바인딩된다

window로 바인딩되지 않는 경우들

1. 객체의 메소드로 사용될 경우 ex. obj.outter();

2. new키워드와 함께 생성자함수로 사용된 경우

function Con () {
	console.log(this.name);
}
new Con(); // undefined

생성된 Con객체로 바인딩되어 Con.name을 바라보았기 때문에 undefined가 뜬다 chrome개발자 콘솔의 기본 name과 다른 값이 된 것이다

3. call, apply, bind

const obj = {
	name: 'osk'
}

function func() {
  console.log(this.name);
}
func(); // ''
func.call(obj); // osk
func.apply(obj); // osk
func.call({ name: 'log91' }) // log91
const binded = func.bind(obj);
binded(); // osk

call과 apply, bind는 위와 같이 명시적으로 어떤 객체를 this바인딩할 것인지를 정해줄 수 있다
call과 apply는 사용방법이 거의 같다 차이점은 첫번째 인자는 바인당할 객체이고 함수이기 때문에 두번째 인자부터는 함수 본연의 매개변수를 담을 수 있다
그 매개변수를 어떤 형식으로 받을지에 대한 차이다

const obj = { name: 'osk' };

function func(age, height) {
  console.log(this.name, age, height);
}
func.apply(obj, [32, 180]); // osk 32 180
func.call(obj, 32, 180); // osk 32 180

bind의 경우에는 함수 호출을 바로하지 않고 바인딩만 시키고 함수를 다시 리턴하기 때문에 호출을 해야 한다

const binded = func.bind(obj, 32, 180);

binded(); // osk 32 180
func.bind(obj, 32, 180)(); // osk 32 180

this는 언제 바인딩되는가

무조건 함수 호출 시점이다 이것을 잘 기억해두는 것이 좋다

// 1
const obj = {
  name: 'osk',
  sayName() {
    console.log(this.name);
  }
}

// 2
const copied = obj.sayName;
copied(); // ''

2의 경우와 같이 사용하게 될 경우에는 호출될 때 this바인딩이 객체의 메소드로 사용된 것이 아니기 때문에 window를 가리키게 된다
함수가 호출될 때 어떻게 되었냐만 잘 본다면 어렵지 않게 바인딩이 어떻게 되는지 찾아낼 수 있다

화살표 함수

this바인딩에 또 다른 예외는 화살표 함수다

const obj = {
  name: 'osk',
  sayName() {
    console.log(this.name);
	const inner = () => {
      console.log(this.name);
    }
    inner();
  }
}

obj.sayName();
// osk
// osk

inner함수 호출의 경우 메소드로 사용된 것이 아니기 때문에 inner함수의 console.log(this.name);의 값은 window.name으로 기대할 수 있는데 화살표함수로 쓰일 경우 상황이 전혀 달라짐을 알아야 한다
화살표 함수 문법의 추가는 ES2015 때다, 이제는 새롭게 추가되었다고 하기엔 너무 오래된 7년전 일이다

화살표 함수는 자신의 부모 함수의 this바인딩을 따라간다
그렇기 때문에 obj.sayName호출 당시 부모 함수 sayName이 바인딩된 obj를 바라보게 되는 것이다
그렇기 때문에 window.name, 즉 '' 값이 아닌 osk가 되는 것이다
하지만 여기서도 아래와 같은 경우를 만든다면 값은 당연히 달라진다

const binded = obj.sayName;
binded();
// ''
// ''

binded 호출은 obj의 함수값을 전역에서 실행한 경우가 되기 때문이다 아래와 같다고 보면된다
메소드로 실행된 것이 아니니 당연히 window객체를 바라보게 되고, inner역시 부모의 바인딩 window를 바라보게 된 것이다

function sayName() {
  console.log(this.name);
  const inner = () => {
    console.log(this.name);
  }
  inner();
}
sayName();
// ''
// ''

결론

처음에 배웠을 때는 lexical scope와 함께 헷갈리는 부분이 많았는데 이제는 사용도 많이 해봐서 그런지 자연스럽게 터득이 되는 것 같다 이번 포스팅도 zerocho님의 유튜브를 보고 나서 작성한 것이긴 하지만 머릿속에 들어와서 그런지 작성 후에는 별도로 영상을 다시 보거나 검색을 하지 않고 있는 그대로 작성을 했다 뭔가 뿌듯

암튼 중요한 것은 호출시점 ! 이다!
호출 시점에 this는 동적으로 바인딩 된다
lexical scoping과는 다른 방식임을 기억하자 끝^_^

Reference

profile
Frontend Developer

0개의 댓글