실행 컨텍스트에서 언급한
VariableEnvironment
와 LexicalEnvironment
에 포함되어있는
ThisBinding
이 여기에 해당한다.
This
는 전역 공간에서는 전역 객체 자체를 의미한다.
예를들어서
브라우저 환경에서는 Window
console.log(this === window) // true
Node.js 환경이라면 global 인것이다.
console.log(this === global) // true
전역변수를 선언하면
자바스크립트 엔진은 이를 전역객체의 프로퍼티로도 할당한다.
변수이자 객체의 프로퍼티인 셈.
var a = 1;
console.log(a) // 1
console.log(window.a) // 1
console.log(this.a) // 1
사실 이러한 동작방식은 당연하다.
자바스크립트의 모든 변수는 특정 개체의 프로퍼티로써 동작하기 때문.
특정 개체란 실행 컨텍스트에서 언급한 LexicalEnvironment
이다.
때문에 정확히 말하자면
전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다
그럼 그냥a
를 출력해도 출력이 가능한 이유는?
마찬가지로 실행 컨텍스트에서 언급한 이유와 같다.
LexicalEnvironment
의 요소인
outerEnvironmentReference
의 특성으로 인한 스코프 체인이 발동되면서
a
라는 요소를 검색하여 전역 객체를 참조하기 때문에
최종적으로 window.a
를 참조하게 되는것이다.
앞서 언급한 내용을 바탕으로는
전역객체에 직접 할당을 하더라도 var
로 전역변수를 선언한것과
겉은 방식으로 동작한다는 사실을 알 수 있다.
하지만 예외사항이 있다.
삭제명령은 사용자가 의도치않게 삭제하는 경우를 방지하기 위해서
자바스크립트 엔진에서 마련한 일종의 방어전략에 의하여
delete
명령어가 사용 가능한 경우는
처음부터 전역객체의 프로퍼티로 할당한 경우에 제한한다.
window.a = 3;
delete window.a // true
console.log(a, window.a , this.a) // error
window.b = 3;
delete b // true
console.log(b, window.b, this.b) // error
때문에 전역변수로 할당한 경우의 delete
명령어는 아래와 같이 동작한다.
var a = 3;
delete a // false
console.log(a, window.a, this.a) // 3, 3, 3
var b = 3;
delete window.b // false
console.log(b, window.b, this.b) // 3, 3, 3
먼저 함수를 어떻게 호출하느냐에 따라서 This
값이 달라진다.
var func = function (x) {
console.log(this, x)
}
func(1) // (Window { ... } 1
---------------------------------
var obj = {
method : func
}
obj.method(2) // { method: f } 2
첫번쨰의 경우는 함수를 호출한 방법이다.
두번째의 경우는 메서드를 호출한 방법이다.
메서드는 객체에 할당한 값이 참조하고있는 함수 func를 참조한다.
간단하게 함수앞에 .
콤마가 있는것으로 구분할 수 있다.
먼저 간단하게 설명하면
위에 기재한 메서드를 구분하는 방법인 .
의 앞에 쓰여진 객체가 this
가 된다.
호출에 대한 주체이기 때문이다.
var obj = {
methodA : function () { console.log(this);},
inner : {
methodB : function () { consoloe.log(this);}
}
}
obj.methodA() // {methodA: f, inner: {...} } ( === obj )
obj.inner.methodB() // {methodB: f} ( === obj.inner )
앞서 설명했듯이 함수를 바로 호출하게되면
호출의 주체, 즉 객체를 명시하지 않고 호출한 것이기 때문에
주체정보를 유추할수 없어 자동으로 이때의 This
는 전역객체를 바라봅니다.
그렇다면 메서드의 내부에서 정의하고 실행한 함수의 This
는 어떨까?
먼저 결론부터 말하자면 이런 경우 또한
오직 .
으로 주체를 명시했는지만 확인하면 된다.
var obj1 = {
outer:function(){
console.log(this);
var innerFunc = function(){
console.log(this);
}
innerFunc(); // 결국은 함수를 호출한것 (Window)
var obj2 = {
innerMethod : innerFunc
};
obj2.innerFunc(); // 호출의 주체인 (obj2)
}
}
obj1.outer(); // 호출의 주체인 (obj1)
정말 허무하고 간단한 방법이지만
명시적으로 this
를 바인딩 시켜주면 됩니다.
var obj = {
outer: function() {
console.log(this);
var innerFunc1 = function(){
console.log(this);
}
}
innerFunc1(); // Window
var self = this;
var innerFunc1 = function(){
console.log(this);
}
innerFunc2(); // obj
}
ES6부터는 함수 내부에서의 this
가 전역객체를 바라보는 문제를 해결하고자
화살표 함수를 도입했다.
화살표함수는 실행 컨텍스트를 생성할 때 this
바인딩 과정 자체를 생략하여
상위 스코프의 this
를 그대로 활용할 수 있다.
var obj = {
outer: function(){
console.log(this) // { outer: f }
var inner = () => {
console.log(this); // { outer: f }
}
inner();
}
}
obj.outer();
콜백함수는 함수A의 제어권을 함수B에 넘겨주게되면 A를 콜백함수라 한다.
이때 this
역시 함수B 내부로직에서 정한 규칙에 따라 값이 결정된다.
B또한 함수이기 때문에 기본적으로 전역객체를 참조하지만
B내부에서 별도로 This
의 대상을 지정한 경우는 해당 대상을 This
로 참조하게됩니다.
setTimeout(function() {console.log(this);},300); // 0.3초뒤 전역객체 출력
[1,2,3,4,5].forEach(function(x){ // 전역객체,각 원소 출력
console.log(this,x);
});
document.body.innerHTML += `<button id='a'>클릭</button>`;
document.body.querySelector('#a')
.addeventListener('click',function(e){
console.log(this,e);
})
// querySelector에서 지정한 엘리먼트, 클릭 이벤트에 대한 정보를 클릭시마다 출력
call
의 첫번째 인자를 This
로 바인딩하는 메서드이다.
객체의 메서드를 호출할 때 또한 call
을 사용하면
원래의 객체를 참조하지 않고 임의로 지정한 객체를 참조한다.
var func = function(a,b,c){
console.log(this,a,b,c);
}
func.call({x:1},1,2,3); // {x:1},1,2,3
기능적으로는 call
과 완전히 동일하지만 인자를 받는 방식만 다름.
call
은 첫번째 인자를 this
로 받고 나머지 인자를 함수의 매개변수로 받지만
apply
는 첫번째 인자를 this
로 받고 두번째 인자 배열의 원소를 함수의 매개변수로 받음.
var func = function(a,b,c){
console.log(this,a,b,c);
}
func.call({x:1},1,2,3); // {x:1},1,2,3
func.apply({x:1},[1,2,3]) // {x:1},1,2,3
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:3}
var arr = Array.prototype.slice.call(obj);
console.log(arr); //[a, b, c, d]
obj
처럼 키가 인덱스값이며 length
프로퍼티의 값이 0 또는 양의정수인 경우
유사배열객체다.
유사배열객체에는 call
또는 apply
를 사용할 수 있다.
slice
는 인자로 시작 인덱스값과 마지막 인덱스 값을 받아서 범위 내의 배열을 반환하지만
인자로 아무값도 넘기지 않으면 원본 배열의 앝은 복사본을 반환한다.
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:2},4,5);
bindFunc2(6,7); // {x:2} 4 5 6 7
bindFunc2(8,9); // {x:2} 4 5 8 9
bind
된 함수를 새로운 변수에 할당하기 때문에
코드를 추적하기에 더욱 수월하다.