3-1. this - 상황에 따라 달라지는 this
1-1. 상황에 따라 달라지는 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. (전역 컨텍스트의 경우, LexicalEnvironment는 전역 객체 그대로 참조)
전역 변수와 전역 객체 프로퍼티
- 선언시 (같은 결과)
window.a = 1;
console.log(a, window.a, this.a); // 1 1 1
- 선언 후 삭제시 (전혀 다른 결과)
(delete 연산자는 window.delete와 같다고 이해하면 됨)
var a = 1;
delete window.a; // false
var b = 2;
delete b; // false
window.c = 3;
delete window.c; // true
window.d = 4;
delete d; // true
console.log(a, window.a, this.a); // 1 1 1
console.log(b, window.b, this.b); // 2 2 2
console.log(c, window.c, this.c); // Uncaught ReferenceError : c is not defined
console.log(d, window.d, this.d); // Uncaught ReferenceError : d is not defined
-> 전역 변수로 선언 시, 자바스크립트 엔진이 이를 자동으로 전역 객체 프로퍼티로 할당하며,
추가적으로 해당 프로퍼티의 configurable(변경 및 삭제 가능성) 속성을 false로 정의하기 때문에 변경 및 삭제를 할 수 없음
메서드로서 호출할 때 그 메서드 내부에서의 this
함수 vs. 메서드
- 앞에 객체가 명시되어 있으면 메서드, 그렇지 않으면 함수
- 독립성에 있어서 서로 차이가 있음
- 함수 : 그 자체로 독립적인 기능을 수행
- 메서드 : 자신을 호출한 대상 객체에 관한 동작을 수행
var func = function(x) {
console.log(this);
};
func(); // window {...} -- 전역 객체 window
var obj = {
method : func;
};
obj.mehtod(); // { method : f } -- 자신을 호출한 객체 obj
-> 익명함수는 그대로인데, 이를 변수에 담아 호출한 경우와 obj 객체의 프로퍼티에 할당해서 호출한 경우의 this가 다름
메서드 앞에 객체를 명시하는(=메서드를 호출하는) 2가지 방법
1) 객체명.메서드명();
2) 객체명['메서드명']();
메서드 내부에서의 this
- 메서드 내부에서의 this는 호출한 주체에 대한 정보를 가리킨다
- 메서드로서 호출시 호출주체는 앞에 있는 객체
var obj = {
methodA : function() {
console.log(this);
},
inner : {
methodB : function() {
console.log(this);
};
}
}
obj.methodA(); // { methodA : f, inner : {...} } -- obj
obj.inner.methodB(); // { methodB : f } -- obj.inner
함수로서 호출할 때 그 함수 내부에서의 this
함수 내부에서의 this
- 함수로서 호출 시 호출주체(객체)를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이라 호출주체 정보 알 수 없음
- 함수에서의 this는 지정하지 않으면 무조건 전역 객체
메서드 내부함수에서의 this
- 함수로서의 호출인지 메서드로서의 호출인지만 파악하면 됨 (함수인데 지정하지 않았다면 this, 메서드라면 호출한 객체)
var obj1 = {
outer : function() {
console.log(this);
var innerFunc = function() {
console.log(this);
}
innerFunc(); // (2)
var obj2 = {
innerMethod : innerFunc;
};
obj2.innerMethod(); // (3)
}
};
obj1.outer(); // (1)
(1) outer() 앞에 obj1.이 있기 때문에 메서드로서의 호출
{ outer : f } ( obj1 바인딩) 출력
(2) innerFunc() 앞에 객체가 없기 때문에 함수로서의 호출
window {...} (전역 객체 바인딩) 출력
(3) innerMethod() 앞에 obj2.가 있기 때문에 메서드로서의 호출
{ innerMethod : f } ( obj2 바인딩 ) 출력
메서드의 내부함수에서의 this를 우회하는 방법 (ES5)
- 호출 주체 없을 경우 자동으로 전역 객체를 바인딩하지 않고 호출 당시 주변 환경의 this를 상속받고 싶을 때는 변수를 활용
var obj = {
outer : function() {
console.log(this);
var innerFunc1 = function() {
console.log(this);
}
innerFunc1(); // window {...}
var self = this; // 변수 선언 : 상위 스코프의 this를 저장해서 내부함수에서 사용
var innerFunc2 = function() {
console.log(this);
}
innerFunc2(); // { outer : f }
}
};
obj.outer(); // { outer : f }
this를 바인딩하지 않는 함수 (ES6 화살표 함수)
- 함수 내부에서 this가 전역 객체를 바라보는 문제를 보완하기 위해 등장
- 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어 상위 스코프의 this를 그대로 활용 가능
var obj = {
outer : function() {
console.log(this);
var innerFunc = () => {
console.log(this);
};
innerFunc(); // { outer : f }
}
};
obj.outer(); // { outer : f }
콜백 함수 호출 시 그 함수 내부에서의 this
콜백함수
- 함수A의 제어권을 다른 함수B(또는 메서드)에게 넘겨주는 경우에서의 함수A를 콜백함수라고 함
- 함수B의 로직에 따라 함수A를 실행하여 this 역시 함수B 내부 로직에 따라 결정됨
- 함수에서의 this는 기본적으로 전역 객체를 참조하지만,
제어권을 받은 함수에서 콜백함수에게 별도로 this가 될 대상을 지정하면 그 대상을 참조함
// (1)
setTimeout( function() {console.log(this);}, 300 );
// (2)
[1,2,3,4,5].forEach( function(x) {consoe.log(this.x);} );
// (3)
document.body.innerText += '<button id="a">클릭</button>';
document.body.querySelect('#a')
.addEventListener('click', function(e) {
console.log(this, e);
});
(1)(2) this 대상 지정하지 않음 -> 전역 객체인 window {...} 출력
(1) 0.3초 후 콘솔창에 window {...} 출력
(2) window{...} 와 각 요소를 한 번씩 총 5번 출력
(3) 앞서 지정한 버튼 엘리먼트와 클릭 이벤트에 관한 정보가 담긴 객체 출력
.addEventLister() 메서드는 기본적으로 콜백함수 호출시 자신의 this를 상속하는 성질이 있음
따라서 호출된 콜백함수 내부의 this는 .addEventListener()의 this인 document.body.querySelect('#a')인 버튼을 가리킴
-> 콜백함수의 제어권을 가지는 함수(메서드)가 콜백함수에서의 this를 무엇으로 할지 결정하고
특별히 정의하지 않은 경우 기본적 함수의 this 처럼 전역 객체를 바라봄
생성자 함수 내부에서의 this
생성자 함수
- 공통된 성질을 지니는 객체들을 생성하는데 사용하는 함수
- 자바스크립트에서 생성자는 클래스(class)를 의미하고, 클래스를 통해 만든 객체는 인스턴스(instance)를 의미함
- 생성자는 구체적인 인스턴스를 만들기위한 일종의 틀이라고 생각하면 됨
( 미리 준비된 공통 속성에 구체적인 인스턴스의 개성을 더해 개별 인스턴스를 만드는 것임 )
- 자바스크립트는 함수에 생성자 역할을 함께 부여해서, new 명령어로 함수 호출하면 생성자로 동작하게 됨
- 생성자 내부 this는 새로 만들 구체적인 인스턴스 자신을 가리킴
구체적인 인스턴스 생성과정
- new 명령어로 함수 호출 시 생성자의 prototype 프로퍼티를 참조하는 __proto__라는 프로퍼티가 있는 객체(인스턴스)를 만들고,
미리 준비된 공통 속성 및 개성을 해당 객체(this)에 부여함
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 인스턴스
console.log(choco, nabi);
/*
결과
Cat { bark:'야옹', name:'초코', age:7 }
Cat { bark:'야옹', name:'나비', age:5 }
*/