뮁이의 개발새발
[코어자바스크립트] Chapter 3. this 본문
Chapter 3. this
this란?
- 자기 자신이 속한 객체를 가리키는 식별자를 참조할 수 있는 키워드
- 메서드의 주인이라고 생각하면 편함
- JS에서는 함수와 객체의 구분이 느슨하기 때문에, 이 둘을 구분하는 거의 유일한 기능임.
상황에 따라 달라지는 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
- 자바스크립트의 모든 변수는 특정 객체의 프로퍼티로서 동작함.
- 어떤 특정 객체의 프로퍼티로 인식하게 됨
- 전역 컨텍스트의 경우 실행 컨텍스트인 LexicalEnvironment는 전역객체를 그대로 참조함.
- 전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다.
- 다만, delete 명령에서는 달라질 수 있음
- window.a로 선언한 변수는 delete 가능 (프로퍼티로 할당)
- var a 로 선언한 변수는 delete 불가능 (전역변수로 할당)
함수 vs 메서드
- 독립성에서의 차이가 있음
- 함수
- 그 자체로 독립적인 기능을 수행
- var func = function() {}
- 메서드
- 자신을 호출한 대상 객체애 관한 동작을 수행
- var obj = { method : func }
함수로서 호출과 메서드로서의 호출을 구분하는 방법
- 함수 앞에 점이 있으면 메서드로서의 호출
- 점이 없으면 함수로서의 호출
- 점 표기법 (obj.method(x)) / 대괄호 표기법(obj’method’) 모두 가능
메서드 내부에서의 this
- 어떤 함수를 메서드로서 호출하는 경우 호출 주체는 바로 함수명(프로퍼티명) 앞의 객체가 됨.
- 점 표기법의 경우 마지막 점 앞에 명시된 객체가 곧 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는 전역 객체를 바라봄.
- 이는 설계상의 오류
- 호출 주체가 없을때는 자동으로 전역객체가 아닌 호출 당시 주변 환경의 this를 그대로 상속받아 사용할 수 있기를 바람 (그것에 스코프 체인과의 일관성을 지키는 것처럼 체감되기 때문)
- 현재 컨텍스트에 바인딩된 대상이 없으면 직전 컨텍스트의 this를 바라보게끔 하면 좋겠음.
내부 함수에서 this를 우회하는 방법
변수를 활용한다.
var self = this; function innerFunction() { console.log(self); // == innerFunction } innerFunction();
화살표 함수를 사용한다.
function outerFunction() { var innerFunction = () => { console.log(this); // innerFunction } innerFunction(); }
화살표 함수 이용 시, 하단과 같이 사용 필요
document.body.innerHTML += '<button id="a">클릭</button>'; document.body.querySelector('#a').addEventListener('click', function(e) { (()=>{ console.log(this); } )(); console.log(this, e); } );
콜백 함수 호출 시 그 함수 내부에서의 this
- 함수 A의 제어권을 다른 함수(또는 메서드) B에게 넘겨주는 경우 함수 A를 콜백함수라고 함
- 제어권을 받은 함수에서는 콜백 함수에 별도로 this가 될 대상을 지정한 경우, 그 대상을 참조하게 됨.
생성자 함수 내부에서의 this
- 생성자 함수 ( = 클래스 )
- 어떤 공통된 성질을 지니는 객체들을 생성하는데 사용하는 함수
- 구체적인 인스턴스를 만들기 위한 틀
- new 명령어와 함께 함수를 호출하면 생성자로서 동작하게 됨.
- 내부에서의 this는 곧 새로 만들 구체적인 인스턴스 자신이 됨.
- ex) 인간
- 인스턴스
- 클래스를 통해 만든 객체
- ex) 직립보행, 언어구사, 도구사용
// 클래스
var Cat = function (name, age){
this.bark = '야옹';
this.name = name;
this.age = age;
}
// 인스턴스
var choco = new Cat('초코',7); // Cat 함수에서의 this는 choco가 됨
var nabi = new Cat('나비', 5);
명시적으로 this를 바인딩하는 방법
// apply 메서드 사용 예시
function.apply(thisArg, [argsArray])
// bind 메서드 사용 예시
function.bind(thisArg, param1, param2, ...)
// 콜백 함수 내에서의 this 지정 예시
callback.call(thisArg, arg1, arg2, ...)
call 메서드와 apply 메서드
call 메서드
Function.prototype.call(thisArg[,arg1[,arg2[,…]]])
메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령
첫번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 함.
그냥 실행하면 this는 전역객체를 참조하지만, call 메서드를 이용하면 임의의 객체를 this로 지정할 수 있음
var func = function (a,b,c){ console.log(this, a,b,c); } func(1,2,3); func.call({x:1},4,5,6); // call 메서드를 이용하면, 임의의 객체를 this로 지정할 수 있음
apply 메서드
Funciton.prototype.apply(thisArg[, argsArray])
call 메서드와 기능적으로 완전히 동일함.
두 번째의 인자를 배열로 받아, 그 배열의 요소들을 호출할 함수의 매개변수로 지정함.
var func = function (a,b,c){ console.log(this, a, b, c); } func.apply({x:1},[4,5,6]);
call/apply 메서드의 활용
유사배열객체에 배열 메서드를 적용
키가 0 또는 양의 정수인 프로퍼티가 존재하고, length 프로퍼티의 값이 0 또는 양의 정수인 객체, 즉 배열의 구조와 유사한 객체의 경우(유사배열객체) call 또는 apply 메서드를 이용해 배열 메서드를 차용할 수 있음
var obj = { 0: 'a', 1: 'b', 2: 'c', length: 3 } Array.prototype.push.call(obj,'d); onsole.log(obj);
단, 문자열의 경우 length 프로퍼티가 읽기 전용이기 때문에, 문자열에 변경을 가하는 메서드는 에러를 던짐.
- ES6에서는 유사배열객체의 데이터타입을 배열로 전환하는 Array.from 메서드를 도입함
생성자 내부에서 다른 생성자를 호출
생성자 내부에 다른 생성자와 공통된 내용이 있을 경우 call 또는 apply를 이용해 다른 생성자를 호출하면 반복을 줄일 수 있음.
function Person(name, gender){ this.name = name; this.gender = gender; } function Student(name,gender,school){ Person.call(this,name,gender); this.school = school; } var by = new Student('민정', 'female','서울대');
여러 인수를 묶어 하나의 배열로 전달하고 싶을 때 - apply 활용
여러 개의 인수를 받는 메서드에게 하나의 배열로 인수들을 전달하고 싶을때는 apply 메서드를 사용
var numbers = [10,20,3,16,45]; var max = Math.max.apply(null, numbers); var min = Math.min.apply(null, numbers); // ES6에서 도입된 펼치기 연산자 활용 시 const max2 = Math.max(...numbers); const min2 = Math.min(...numbers);
bind 메서드
call과 비슷하지만, 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드
함수에 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 }); // {x:1}을 func의 this로 지정? 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 bindFunc2(8, 9); // { x: 1 } 4 5 8 9
c. name 프로퍼티
name 프로퍼티에 bound라는 접두어가 붙음
기존의 call이나 apply보다 코드를 추적하기에 더 수월함
상위 컨텍스트의 this를 내부함수나 콜백함수에 전달하기
call, apply, bind를 활용하면 편리
// call var obj = { outer: function() { console.log(this); var innerFunc = function() { console.log(this); }; innerFunc.call(this); }, }; obj.outer();
// bind var obj = { outer: function() { console.log(this); var innerFunc = function() { console.log(this); }.bind(this); innerFunc(); }, }; obj.outer();
콜백함수를 인자로 받는 함수나 메서드 중에서 콜백함수 내에서의 this에 관여하는 함수/메서드에 대해서도 bind를 이용하면 this의 값을 지정할 수 있음
var obj = { logThis: function() { console.log(this); }, logThisLater1: function() { setTimeout(this.logThis, 500); }, logThisLater2: function() { setTimeout(this.logThis.bind(this), 1000); }, }; obj.logThisLater1(); // Window { ... } obj.logThisLater2(); // obj { logThis: f, ... }
- 화살표 함수의 예외사항
- ES6에서의 화살표 함수는, this를 바인딩하는 과정이 제외되었음
- 접근하고자 하면 스코프체인상 가장 가까운 this에 접근하게 됨.
별도의 인자로 this를 받는 경우(콜백 함수 내에서의 this)
콜백함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체(thisArg)를 인자로 지정할 수 있는 경우가 있음
메서드의 thisArg값을 지정하면 콜백 함수 내부에서 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); // 여기서 this를 report로 바인딩 }, average: function() { return this.sum / this.count; }, }; report.add(60, 85, 95); console.log(report.sum, report.count, report.average()); // 240 3 80
정리
- 묵시적 this 바인딩
- 전역공간: 전역객체
- 함수를 메서드로 호출: 호출 주체
- 함수를 함수로 호출: 전역 객체 (메서드 내부 함수도 똑같다)
- 함수를 생성자로 호출: 생성될 인스턴스
- 콜백 내부 함수: 제어권을 넘겨받은 함수가 정의한 바에 따름
- 명시적 this 바인딩
- call, apply 메서드: this/인자를 지정하면서 호출
- bind: this/인자를 지정하면서 새로운 함수 생성
- 순회 메서드: 별도 인자로 this를 받음
- forEach, map, filter 등등
'Front-end > Javascript' 카테고리의 다른 글
[코어자바스크립트] Chapter 5. 클로저 (0) | 2024.05.18 |
---|---|
[코어자바스크립트] Chapter 4. 콜백함수 (0) | 2024.05.18 |
[코어 자바스크립트] Chapter 2. 실행 컨텍스트 (0) | 2024.04.08 |
[코어 자바스크립트] Chapter 1. 데이터 타입 (1) | 2024.04.08 |
[Javascript] jQuery Event (0) | 2021.09.06 |