///////
Search

this (1)

다른 언어와 다르게 JavaScript에서는 this는 어디서나 사용이 가능하다. (혼란의 원인)

상황에 따라 달라지는 this

자바스크립트의 this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정
즉, 함수를 호출할 때 결정

1. 전역 공간에서의 this

전역 객체를 가리킨다.
브라우저 → window
Node.js → global
전역에서 변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당
var a = 1; console.log(a); // 1 console.log(window.a); // 1 console.log(this.a); // 1
JavaScript
복사
자바스크립트의 모든 변수는 실은 특정 객체의 프로퍼티로서 동작
특정 객체란 실행 컨텍스트의 Lexical Environment를 의미
실행 컨텍스트는 변수를 수집하여 Lexical Environment의 프로퍼티로 저장
var a = 1; window.b = 2; console.log(a, window.a, this.a); // 1 1 1 console.log(b, window.b, this.b); // 2 2 2
JavaScript
복사
But, 삭제는 다르다.
var a = 1; delete window.a; // false console.log(a, window.a, this.a); // 1 1 1 var b = 2; delete b; // false console.log(b, window.b, this.b); // 2 2 2 window.c = 3; delete window.c; // true console.log(c, window.c, this.c); // c is not defined window.d = 4; delete d; // true console.log(d, window.d, this.d); // d is not defined
JavaScript
복사
→ 전역객체의 프로퍼티로 할당한 경우에는 삭제가 된다.
→ 전역변수로 선언한 경우에는 삭제가 되지 않는다.

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

함수 vs 메서드
1.
함수 → 그 자체로 독립적인 기능을 수행
2.
메서드 → 자신을 호출한 객체에 관한 동작을 수행
a.
메서드는 객체의 메서드로서 호출할 경우에만 메서드로 동작, 아니면 함수로 동작
var func = function (x) { console.log(this, x); }; func(1); // window { ... } 1 var obj = { method: func }; obj.method(2); // { method: f } 2
JavaScript
복사
점 표기법, 대괄호 표기법으로 호출한 함수는 메서드로 동작한다.
메서드 내부에서의 this에는 호출한 주체에 대한 정보가 담긴다.
var obj = { methodA: function () { console.log(this); }, inner: { methodB: function () { console.log(this); }, } }; obj.methodA(); // obj obj['methodA'](); // obj obj.inner.methodB(); // obj.inner obj.inner['methodB'](); // obj.inner
JavaScript
복사

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

함수 내부에서의 this → 전역 객체를 가리킨다.
메서드의 내부함수에서의 this → 메서드를 호출한 객체를 가리킨다.
var obj1 = { outer: function () { console.log(this); // obj1 var innerFunc = function () { console.log(this); } innerFunc(); // window var obj2 = { innerMethod: innerFunc }; obj2.innerMethod(); // obj2 } }; obj1.outer();
JavaScript
복사
→ 함수 실행 당시의 주변 환경은 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점, 대괄호 표기가 있는지가 관건
메서드 내부 함수에서의 this를 우회하는 방법
외부 환경에서 this를 변수에 저장하고 이를 함수 내부에서 호출한다.
this를 바인딩하지 않는 함수 → 화살표 함수
화살표 함수는 상위 스코프의 this를 활용할 수 있다.
var obj = { outer: function () { console.log(this); // obj var innerFunc = () => { console.log(this); }; innerFunc(); // obj } }; obj.outer();
JavaScript
복사

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

함수 A의 제어권을 다른 함수 B에 넘겨주는 경우 함수 A를 콜백 함수라고 한다.
addEventListener 메서드는 콜백 함수를 호출할 때 자신의 this를 상속하도록 정의

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

생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수
생성자 함수로서 호출된 경우는 내부에서의 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);
JavaScript
복사

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

상황별로 this에 다르게 값이 바인딩되는 것을 의도적으로 바꾸는 방법!

1. call 메서드

메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령
call 메서드의 첫 번째 인자를 this로 바인딩
이후의 인자들은 호출할 함수의 매개변수
call 메서드를 이용하면 임의의 객체를 this로 지정이 가능하다.
Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])
JavaScript
복사
// 일반 함수의 경우 원래 this는 전역, call을 사용하면 첫 번째 인자 var func = function (a, b, c) { console.log(this, a, b, c); } func(1, 2, 3); // window 1 2 3 func.call({ x: 1 }, 4, 5, 6); // { x: 1 } 4 5 6 // 객체의 메서드의 경우 원래 this는 호출한 객체, call을 사용하면 첫 번째 인자 var obj = { a: 1, method: function (x, y) { console.log(this.a, x, y); } }; obj.method(2, 3); // 1 2 3 obj.method.call({ a: 4 }, 5, 6); // 4 5 6
JavaScript
복사

2. apply 메서드

call 메서드와 기능적으로 완전 동일
call 메서드는 나머지 모든 인자들을 호출할 함수의 매개변수로 지정
apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정
var func = function (a, b, c) { console.log(this, a, b, c); }; func.apply({ x : 1 }, [4, 5, 6]); // { x : 1 } 4 5 6
JavaScript
복사

3. call / apply 메서드 활용

유사배열객체에 배열 메서드 적용 (Array.from 으로도 가능)
유사배열객체 - 키가 0 또는 양의 정수인 프로퍼티 존재 (배열의 성질을 다 가지고 있지만 객체)
length 프로퍼티의 값이 0 또는 양의 정수
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'] // slice 메서드에 매개변수를 넘기지 않으면 얕은 복사
JavaScript
복사
함수 내부의 arguments 객체와 NodeList에도 활용 가능
// arguments 객체에 배열 메서드 사용 function a () { var argv = Array.prototype.slice.call(arguments); argv.forEach(function (arg) { console.log(arg); }); } a(1, 2, 3); document.body.innerHTML = '<div>a</div><div>b</div><div>c</div>'; var nodeList = document.querySelectorAll('div'); var nodeArr = Array.prototype.slice.call(nodeList); // nodeList에 배열 메서드 사용 nodeArr.forEach(function (node) { console.log(node); });
JavaScript
복사
단, 문자열의 경우 length 프로퍼티가 읽기 전용이므로 사용이 불가능하다.
유사 배열 객체에 배열 메서드를 활용하기 위해 es6에서는 Array.from이 도입
생성자 내부에서 다른 생성자를 호출
여러 인수를 묶어 하나의 배열로 전달하고 싶을 때 apply 활용
→ apply 메서드는 두 번째 인자로 전달한 배열을 펼치는 역할을 한다.
→ es6에서는 스프레드 연산자를 통해 간편하게 작성 가능

bind 메서드

call과 비슷하지만 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드
bind 메서드를 통해 영원히 커스텀 this를 가지는 함수를 만들 수 있다. → 새로운 함수를 반환하기 때문
var func = function (a, b, c, d) { console.log(this, a, b, c, d); }; func(1, 2, 3, 4); // window 1 2 3 4 var bindFunc1 = func.bind({ x: 1 }); bindFunc1(5, 6, 7, 8); // { x : 1 } 5 6 7 8 var bindFunc2 = func.bind({ x: 1 }, 4, 5); bindFunc2(6, 7); // {x : 1 } 4 5 6 7
JavaScript
복사
name 프로퍼티 → bind 메서드를 적용해서 새로 만든 함수는 name 프로퍼티에 bound 접두어가 붙는다.
상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달할 수 있다.
1.
call 메서드
var obj = { outer: function () { console.log(this); var innerFunc = function () { console.log(this); }; innerFunc.call(this); } }; obj.outer();
JavaScript
복사
2.
bind 메서드
var obj = { outer: function () { console.log(this); var innerFunc = function () { console.log(this); }.bind(this); // 아예 커스텀 this를 지니는 새로운 함수를 반환했다. innerFunc(); } }; obj.outer();
JavaScript
복사

화살표 함수의 예외사항

화살표 함수 내부에는 this가 아예 없으며, 접근하고자 하면 스코프체인상 가장 가까운 this에 접근한다.
call / apply / bind 메서드 사용할 필요 없어짐 (편리)
var obj = { outer: function () { console.log(this); var innerFunc = () => { console.log(this); // outer }; innerFunc(); } }; obj.outer();
JavaScript
복사

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

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); }, average: function () { return this.sum / this.count; } }; report.add(60, 85, 95); console.log(report.sum, report.count, report.average()); // 240 3 80
JavaScript
복사