[TIL 10][JS] this

개요

this는 뭐라고 정의해야 맞는걸까? 그냥 뜻 그대로 이것! 이라고 해야하나.. 나를 부른게 누구냐라고 하는게 젤 쉬울거 같다.
처음 자바스크립트 공부하면서 막히는 부분이 thisprototype이 아닐까 싶다.
물론 이걸 작성하고 있는 나도 공부하기 위해서 작성하는거라 쓰면서 점차 이해도를 높히려고 노력하고있다.

thisthis를 포함하고 있는 함수가 어떻게 실행되느냐에 따라 결정된다. 즉, 함수 선언 시점에 의해 결정되는 값이 아닌 함수 실행 시점에 결정되는 값이다.

const age = 32;

const jay = {
	age: 31;
	logAge: function () {
		console.log(this.age);
	}
};

const func = jay.logAge();

jay.logAge();
func(); 

위와 같은 경우 jay.logAge()func()는 같은 함수를 실행하지만, 콘솔의 출력결과는 다르다.
함수가 실행될 수 있는 방식에는 4가지가 있는데 this의 값 또한 4가지 경우의 수가 있다.

1. Regular Function Call
2. Dot Notation (Object Method Call)
3. Call, Apply, Bind
4. "new" keyword

1. Regular Function Call

예시

const age = 32;

function call() {
	console.log(this.age); // 32
}

call();
  • 일반적인 함수 호출에서의 this는 global 객체를 가르킨다.
  • 일반적인 함수에서의 this
    1. Non strict mode: Global Object
    2. Strict Mode: undefined
const age = 32;

const jay = {
	age: 31,
	logAge: function () {
		console.log(this.age);
	}
};

const func = jay.logAge;
func();
  • 위와 같은 경우에 func() 로 호출을 하면 jay 객체 안의 logAge가 일반 함수로 실행이된다.그래서 출력값은 global object인 32가 출력된다

2. Dot Notation

예시

const age = 32;

const jay = {
	age: 31,
	logAge: function () {
		console.log(this.age);
	}
};

const func = jay.logAge;

// Dot Notation
jay.logAge();

// Regular Function Call
func();
  • Dot Notation은 함수선언식으로 만든 함수명에 .을 붙여서 실행하는 방법이다.
  • Dot Notation에서는 Regular Function Call과는 달리 .앞의 함수명이 this가 된다. 그래서 jay.logAge()의 결과값은 31, func()의 결과값은 32가 된다.

일반함수와 Dot Notation의 비교 예시

일반 함수

const age = 100;

function verifyAge () {
  return this.age > 21;
}

const jay = {
  age: 20,
  verifyAge: verifyAge
};

const beerShop = {
  sellBeer: function (customer) {
    if (!verifyAge()) {
      return "No Beer";
    }
    
    return "Beer";
  }
};

beerShop.sellBeer(jay);
  1. beerShop.sellBeer(jay)으로 함수 호출
  2. beerShop 매개변수로 4번째줄 jay가 불러짐
  3. 조건문 안의 verifyAge()로 나이 인증
  4. verifyAge()는 일반 함수 호출을 하였으므로 this는 global age 변수가 된다.
  5. global age는 100이므로 "Beer"가 return 됨

Dot Notation

const age = 100;

function verifyAge () {
  return this.age > 21;
}

const jay = {
  age: 20,
  verifyAge: verifyAge
};

const beerShop = {
  sellBeer: function (customer) {
    if (!customer.verifyAge()) {
      return "No Beer";
    }
    
    return "Beer";
  }
};

beerShop.sellBeer(jay);
  1. beerShop.sellBeer(jay)로 함수 호출
  2. beerShop 매개변수로 jay가 호출
  3. !customer.verifyAge()로 나이 인증
  4. !customer.verifyAge()는 Dot notation으로 호출되었으므로 jaythis가 됨
  5. jayage는 20이므로 "No Beer"가 return 됨

3. call, apply, bind

call

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/call

  • call 메서드는 일반 함수 실행과 똑같이 실행을 하는데, 차이점이 있다면 첫번째 매개변수에 this값을 넣어서 호출할 수 있다는 것이다.
  • call 메서드의 첫번째 arg는 this값으로 사용되고 두번째 arg부터는 전부 call한 함수로 전달한다. 그리고 call 메서드의 arg 갯수 제한은 없다

예시

function logAge() {
	console.log(this.age);
};

const person = {
	age: 20;
};

logAge.call(person)
  1. call 메서드를 통해 person 변수를 this로 binding한다.
  2. logAge()this값은 person이 된다
  3. console.log를 통해 this.age값을 호출하면 personage인 20이 출력된다.

예시2

function foo (a, b, c) {
	console.log(this.age);
	console.log(a + b + c);
};

const jay = {
	age: 32;
}

foo.call(jay, 1, 2, 3);
  • 위와 같은 경우에서는 jaythis값으로 잡히고, 1, 2, 3이 매개변수로 넘어가서 결과값은 32와 6이 된다

apply

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

  • apply 메서드도 call과 같이 첫번째 args는 this가 된다.
  • applycall의 차이점은 아래와 같다.
  1. call은 첫번째 arg를 제외한 나머지를 전부 전달하고, apply는 첫번째 arg를 제외한 나머지를 배열로 처리해서 보낸다.
  2. call은 arg 갯수 제한이 없는 반면에, apply는 2개의 args만 받을 수 있다.
function foo (a, b, c) {
	console.log(this.age);
	console.log(a + b + c);
};

const jay = {
	age: 32
};

foo.apply(jay, [1, 2, 3]);
  • 위의 call과 결과값이 같지만 apply는 두번째 매개변수를 배열로 보냈다.

bind

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

  • bind 메서드는 원본 함수를 복제한 새로운 함수를 반환한다.

예시

function foo () {
	console.log(this.name);
}

const who = {
	name: jay
};

const bar = foo.bind(who);

bar();
  • bindcall처럼 받을수 있는 arg의 갯수에 대한 제한이 없다. 마찬가지로 첫번째가 this값으로 설정되고, 나머지는 매개변수이다.
  • 위의 코드에서 foo.bind(who)에서 foo 함수를 실행시키지는 않고, this값과 나머지 값들을 저장한 후, 새로운 함수를 리턴한다. 위 코드에서 실행되는 곳은 bar()이다.

예시 2

function foo (a, b, c) {
	console.log(this.age);
	console.log(a + b + c);
};

const jay = {
	age: 32;
};

const bar = foo.bind(jay, 1);

bar(2, 3);
  1. foo.bind(jay, 1)jay,1을 바인딩시킨 foo함수를 변수 bar에 저장
  2. bar(2, 3)으로 함수 실행
  3. 기존에 바인딩 시킨 jaythis값이 되고 1은 첫번째 매개변수(a), bar(2, 3)을 통해 bar 함수를 실행시키며, 2, 3은 각각 두번째(b), 세번째(c) 매개변수가 된다.
  4. 첫번째 줄의 결과값은 this의 대상이 된 jay의 age인 32, 두번째 줄은 함수 실행에서 foo.bind()에서 매개변수로 넘어온 1 그리고 bar()를 실행시키며 매개변수로 넘어온 2, 3을 더하여 6이 된다.

4. new keyword

function Person (name) {
	this.name = name;
	console.log(this);
}

new Person("jay");
  • new 키워드로 함수를 사용하면 생성자 함수 라고 불린다.
  • 생성자 함수를 만들 때는 함수명을 대문자로 시작하는 표기법이 있다.
  • new 키워드를 사용한 함수 내부에서 this를 사용할 경우, this의 값은 빈 객체가 된다. 저 위에서 this.name = name을 해줘서 this객체에 name 프로퍼티가 들어간것이다.
  • Person함수를 보면 return이 따로 명시되어 있지않다. 그럼 원래 내가 알고있는데로라면 return값은 undefined가 되야한다. 하지만 생성자 함수에서는 return이 명시되어있지 않아도 this를 return한다

좋은 웹페이지 즐겨찾기