Language/Javascript

Javascript - this

둉이 2021. 11. 28. 23:39

 

This

  • 실행 컨텍스트가 생성될 때(=함수가 실행될 때) 결정됨
    • 실행 컨텍스트 생성 당시 this가 지정되지 않은 경우에는 전역 객체가 대신 저장됨
  • 자바에서의 this와는 다른 개념
    • 자바에서는 클래스로 생성한 인스턴스 객체를 의미
    • 자바스크립트에서는 어디서든 this라는 개념이 사용될 수 있음

 

전역 공간에서의 This

  • 전역 객체를 가리킴
    • 브라우저에서는 window이고, node.js에서는 global
  • 전역 변수 생성 시, 자바스크립트 엔진은 해당 변수를 전역 객체의 프로퍼티로 할당
var a = 1;
console.log(a);  // 1
console.log(window.a);  // 1
console.log(this.a);  // 1

// → 스코프 체인을 탐색하면서 전역(window)객체의 프로퍼티를 탐색하므로 앞에 window를 붙이지 않아도 됨!

 

  • 전역 객체에 직접 프로퍼티 선언 ≠ 직접 변수 선언
    • 전역 객체에 직접 프로퍼티를 할당하는 경우에는 delete 키워드로 삭제가 가능
    • 그 외 호이스팅 여부에서도 차이점을 가짐

 

메소드 내부에서의 This

  • 함수 ≠ 메소드
    • 독립성에서 차이를 둠
    • 함수는 독립적으로 실행 가능, 메소드는 호출된 대상 객체에 관한 동작을 수행하므로 종속적
  • 메소드 함수 실행 시 this는 해당 메소드를 실행한 주체가 됨
var obj1 = {
	outer: function() {
		console.log(this);  // obj1
		var innerFunc = function() {
			console.log(this);  // window
		}
		innerFunc();
		var obj2 = {
			innerMethod: innerFunc
		};
		obj2.innerMethod();  // obj2
	}
}
obj1.outer();

// → 첫 번째 this는 obj1에 종속되어 있으므로 this는 obj1
// → 두 번째 this는 해당 함수를 실행한 주체가 없으므로 this는 window(전역 객체)
// → 세 번째 this는 obj2에 종속되어 있으므로 this는 obj2

 

 

  • 메소드 내부의 메소드에서의 this 호출
var obj = {
	methodA: function() {
		console.log(this);
	}
	inner: {
		methodB: function() {
			console.log(this);
		}
	}
}
obj.methodA();  // obj
obj.inner.methodB();  // obj.inner

// → 메소드 내의 메소드에서의 this는 해당 메소드를 호출한 메소드가 됨

 

함수에서의 This

  • 함수로서의 호출에서는 this가 지정되지 않음 → window 반환
  • 함수로서의 This 호출과 메소드로서의 This 호출 구분
    • 메소드로서 함수를 호출하면 해당 메소드가 종속된 객체를 this로 반환
var obj1 = {
	outer: function() {
		console.log(this);
		var innerFunc = function() {
			console.log(this);
		}
		innerFunc();
		var obj2 = {
			innerMethod: innerFunc
		};
		obj2.innerMethod();
	}
}
obj1.outer();

// → 차례대로 obj1, window, obj2 반환

 

 

생성자 함수(or Class)에서의 This

  • new 명령어와 함께 함수를 호출하면 해당 함수가 생성자로서 동작
  • 어떤 함수가 생성자 함수로서 호출된 경우에는 내부에서의 this는 곧 인스턴스 자신이 됨
var Cat = function(name, age) {
	this.bark = '야옹';
	this.name = name;
	this.age = age;
};

var choco = new Cat('초코', 7);
var nabi = new Cat('나비', 5);
console.log(choco, nabi);

 

명시적으로 This를 바인딩하는 방법

1. call

함수명.call(thisArg [, arg1, ...])
  • 메소드의 호출 주체인 함수를 즉시 실행하도록 하는 명령

 

2. apply

함수명.apply(thisArg [, argArr])
  • call 메소드와 동일하게 동작
  • 차이점
    • 두 번째 파라미터를 배열로 받아서 그 배열의 요소들을 호출할 함수의 매개변수로 지정

 

3. bind

함수명.bind(thisArg [, arg1, ...])
  • call, apply와는 달리 즉시 호출하지는 않고 넘겨 받은 this와 파라미터들을 바탕으로 새로운 함수 반환
  • 이 때 반환된 함수는 name 프로퍼티에 bound라는 접두어가 붙는다.

 

call, apply 메소드의 활용법

  • 유사 배열 객체에 배열 메소드 적용
    • 원래 Object나 NodeList 등 배열 메소드가 적용되지 않는 객체에 call 또는 apply 메소드를 이용하여 사용 가능하도록 처리
var obj = {
	0: 'a',
	1: 'b',
	2: 'c',
	length: 3,
};
// Object 객체 뒤에 요소 push
Array.prototype.push.call(obj, 'd');  // 3: 'd' 추가

// Object 객체 복사 + 배열로 반환
var arr = Array.prototype.slice.call(obj);  // ['a', 'b', 'c', 'd']

// NodeList에 배열 메소드 적용
var nodeList = document.querySelector('div');
var nodeArr = Array.prototype.slice.call(nodeList);

// → ES6 이상 문법에서는 Array.from()을 이용하여 배열 복사 가능

 

 

  • 문자열의 경우에는 length 프로퍼티를 수정할 수 없기 때문에 push/pop, shift/unshift, splice 등은 사용할 수 없음
    • map, every, some, reduce 등은 사용 가능
// 문자열 뒤에 map 메소드 적용
var str = '안녕 난 미정';
Array.prototype.map.call(str, (e, i) => `${e} ${i}`);ㅋㅏ메

 

  • 생성자 내부에서 다른 생성자 호출
    • 생성자 함수에서 class의 상속처럼 공통된 다른 생성자 함수를 호출할 수 있음
function Person(name, gender) {
	this.name = name;
	this.gender = gender;
}
function Student(name, gender, school) {
	Person.call(this, name, gender)
	this.school = school;
}
function Employee(name, gender, company) {
	Person.call(this, name, gender)
	this.company= company;
}

var mj = new Student('미정', 'female', '하버드대');
var mj2 = new Employee('둉이', 'male', '당근마켓');

 

  • 여러 인수를 묶어 하나의 배열로 전달하고자 할 때
    • Math.max()나 Math.min() 함수는 ES6 이상 문법에서는 스프레드 연산자를 이용하여 배열을 전달할 수 있으나 ES5 이하 문법에서는 사용 불가
    • 다음과 같이 apply 함수를 사용하여 배열에 적용 가능 
      • var numbers = [1, 2, 3, 4, 5];
      • console.log(Math.min.apply(null, numbers));

 

화살표 함수에서의 This

  • 화살표 함수는 this를 바인당하는 과정이 사라짐(=함수 내부에 this가 없음)
  • 화살표 함수 내에서 this를 호출하면 스코프 체인 상 가장 가까운 this에 접근하여 반환
var obj = {
	outer: function() {
		console.log(this);  // outer 함수 자체를 반환
		var innerFunc = () => {
			console.log(this);  // 가장 가까운 스코프인 outer의 this인 outer 함수 자체를 반환
		}
	}
}
obj.outer();

 

별도의 인자로 This를 받는 경우(콜백 함수 내의 this)

  • forEach, map, filter, some, every, find 등에서 콜백 함수 다음 두 번째 인자로 thisArg를 지정할 수 있음
var report = {
	sum: 0,
	count: 0,
	add: function() {
		var args = Array.prototype.slice.call(arguments);
		args.forEach(function(entry) => {
			this.sum += entry;
			++this.count;
		}, this);
	}
}
report.add(60, 85, 95);
console.log(report.sum, report.count);  // 240, 3 출력