11일차 자바스크립트 문법(this 정리)

seul-bean·2023년 5월 27일
0

Today I learned

목록 보기
11/40
post-thumbnail

오늘은 지금까지 배웠던것 중 제일 어려웠던 this를 정리하면서 복습해보려 한다.

오늘의 study!!!
JavaScript 문법 종합반 3주차 (this)


this(정의, 활용방법, 바인딩, call, aplly, bind)



(1) 상황에 따라 달라지는 this


1. this는 실행 컨텍스트가 생성될 때 결정된다.

이 말을 this를 bind한다(=묶는다) 라고도 한다.
this는 함수를 호출할 때 결정된다.

전역공간에서의 this는 전역 객체를 가리킨다.
런타임 환경에 따라 this는 window(브라우저 환경) 또는 global(node 환경)을 각각 가리킴.

2. 메서드로서 호출할 때 그 메서드 내부에서의 this

함수 vs 메서드 기준은 독립성!!
함수: 그 자체로 독립적인 기능을 수행.

함수명();

메서드: 자신을 호출한 대상 객체에 대한 동작을 수행 (종속적)

객체.메서드명()

함수로서의 호출과 메서드로서의 호출 구분 기준: . []

var obj = {
	method: function (x) {console.log(this, x)};
};
obj.method(1);	// {method: f} 1
obj.['method'](2);	// {method: f}	2

3. 함수로서 호출할 때 그 함수 내부에서의 this

함수 내부에서의 this
함수로서 '독립적으로' 호출할 때는 this는 항상 전역객체를 가리킨다.

메서드의 내부함수에서의 this
메서드의 내부라고 해도, 함수로서 호출한다면 this는 전역객체를 의미.


❗this 바인딩에 관해서는 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지)은 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지가 관건이라는 것을 알 수 있음.

메서드의 내부 함수에서의 this 우회
a. 변수를 활용하는 방법
내부 스코프에 이미 존재하는 this를 별도의 변수(ex: self)에 할당하는 방법.

var obj1= {
	outer: function(){
    	console.log(this);	// (1) outer
    
    	// AS-IS
    	var innerFunc1 = function() {
    		console.log(this);	// (2) 전역객체
    	};
    	innerFunc1();
      
      	// TO-BE
      	var self = this;
      	var innerFunc2 = function() {
          console.log(self);	// (3) outer
        };
      	innerFunc2();
  	}
};

// 메서드 호출 부분
obj1.outer();

b. 화살표 함수(=this를 바인딩하지 않는 함수)

일반 함수와 화살표 함수의 가장 큰 차이점은? this bindind 여부

var obj = {
	outer: function() {
    	console.log(this);	// (1) obj
        var innerFunc = () => {
        	console.log(this);	// (2) obj
        };
        innerFunc ();
    }
}

obj.outer();

ES6에서 처음 도입된 화살표 함수는, 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 없다.
(따라서, this는 이전의 값-상위값-이 유지된다. / ES6에서는 함수 내부에서 this가 전역객체를 바라보는 문제 때문에 화살표함수를 도입했다!)

4. 콜백 함수 호출 시 그 함수 내부에서의 this

콜백함수 "어떠한 함수, 메서드의 인자(매개변수)로 넘겨주는 함수"

콜백함수도 함수기 때문에 this는 전역 객체를 참조하지만 (호출주체가 없기 때문)
콜백함수를 넘겨받은 함수에서 콜백 함수에 별도로 this를 지정한 경우는 예외적으로 그 대상을 참조하게 되어 있음.

a. setTimeout 함수, forEach 메서드는 콜백 함수를 호출할 때 대상이 될 this를 지정하지 않으므로, this는 곧 window객체

// 별도 지정 없음: 전역객체
setTimeout(function () {console.log(this)}, 300);

// 별도 지정 없음: 전역객체
[1, 2, 3, 4, 5].forEach(function(x) {
  console.log(this, x);
});

b. addEventLister 안에서의 this는 항상 호출한 주체의 element를 return하도록 설계되었음.

// this는 버튼을 의미함.
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function(e) {
	console.log(this, e);
});

5. 생성자 함수 내부에서의 this

생성자(constructer) : 구체적인 인스턴스(어려우면 객체로 이해!)를 만들기 위한 일종의 틀 (붕어빵 틀 같은 것!!)

var Cat = function (name, age) {
	this.bark = '야옹';
    this.name = name;
    this.age = age;
};

var choco = new cat('초코', 7);	// this : choco
var nabi = new Cat('나비', 5);	//this : nabi

새로운 인스턴스가 생성이 될 때마다 this는 달라진다.





(2) 명시적 this 바인딩


자동으로 부여되는 상황별 this의 규칙을 깨고 this에 별도의 값을 저장하는 방법.

1. call 메서드

a. 호출 주체인 함수를 즉시 실행하는 명령어.
b. call명령어를 사용하여, 첫 번째 매개변수에 this로 binding할 객체를 넣어주면 명시적으로 binding 할 수 있음.

var func = function (a, b, c) {
	console.log(this, a, b, c);
};

// no binding
func(1, 2, 3);	//Window{...} 1 2 3

// 명시적 binding
// func 안에 this에는 {x:1}이 binding돼요
func.call({x:1}, 4, 5, 6};	// {x:1} 4 5 6

2. apply 메서드

call 메서드와 완전히 동일! 다만, this에 binding할 객체는 똑같이 넣어주고 나머지 부분만 배열 형태로 넘겨줌.

var func = function (a, b, c) {
	console.log(this.x, a, b, c);
};
func.apply({x:1}, [4, 5, 6]);	// 1 4 5 6

3. call/apply 메서드 활용

this binding을 위해 사용하기도 하지만 더 유용한 측면도 있음.

유사배열객체(array-like-object)에 배열 메서드를 적용

유사배열의 조건
1. 반드시 length가 필요해야한다.
2. index번호가 0번부터 시작해서 1씩 증가해야한다.

// 객체에는 배열 메서드를 직접 적용할 수 없다.
// 유사배열객체에는 call 또는 apply 메소드를 이용해 배열 메서드를 차용할 수 있다.
var obj = {
	0: 'a',
    1: 'b',
    2: 'c',
    length: 3
};
Array.prototype.push.call(obj, 'd');
console.log(obj);	// { 0: 'a', 1: 'b', 2: 'c', 3: 'd', length:4 }

var arr = Array.prototype.slice.call(obj);
console.log(arr);	// ['a', 'b', 'c', 'd']

사실, call/apply를 통해 this binding을 하는 것이 아니라 객체 -> 배열로의 형 변환 만을 위해서도 쓸 수 있지만 원래 의도와는 거리가 먼 방법이라 할 수 있음.

따라서, ES6에서는 Array.from이라는 방법을 제시함.

Array.from 메서드(ES6)

// 유사배열
var obj = {
	0: 'a',
	1: 'b',
    2: 'c',
	length: 3
);

// 객체 -> 배열
var arr = Array.from(obj);

// 찍어보면 배열이 출력됩니다.
console.log(arr);

4. bind 메서드

call과는 다르게 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하는 메서드.

목적 - 함수에 this를 미리 적용. 부분 적용 함수 구현할 때 용이

var func = function (a, b, c, d) {
  console.log(this, a, b, c, d);
};
func(1, 2, 3, 4); // node객체

// 함수에 this를 미리 적용!!
var bindFunc1 = func.bind({ x: 1 });
bindFunc1(5, 6, 7, 8);

// 부분 적용 함수
var bindFunc2 = func.bind({ x: 1 }, 4, 5);
bindFunc2(6, 7);

// global{} 1 2 3 4
// {x:1} 5 6 7 8
// {x:1} 4 5 6 7

name 프로퍼티
bind 메서드를 적용해서 새로 만든 함수는 name 프로퍼티에 'bound'라는 접두어가 붙는다. (추적하기가 쉬움)

var func = function (a, b, c, d) {
  console.log(this, a, b, c, d);
};

var bindFunc1 = func.bind({ x: 1 });

var bindFunc2 = func.bind({ x: 1 }, 4, 5);
bindFunc2(6, 7);

console.log(func.name); // func
console.log(bindFunc1.name); // bound func
console.log(bindFunc2.name); //bound func

// name 프로퍼티!!
// bind - 'bound', 라는 접두어!



정리하면서도 계속 VScode에 쳐보면서 다시 해봤는데도 여전히 this는 복잡하고 어렵다😭 언젠가 익숙해지는 날이 오겠지...???
profile
안녕하세요 성장하는 새싹 프론트엔드 개발자 입니다🌱

0개의 댓글