
this
현재 함수를 호출한 객체를 말한다.
전역 문맥에서 this를 호출해보면 Window타입의 window라는 객체를 리턴한다.
(객체 타입명은 대문자 W, 변수명은 소문자 w인듯 하다.)
웹 브라우저에서는 window 가 최상단의 전역 객체이고, 이 객체는 모든 전역 함수/변수를 포함하고 있다.
console.log(this === window); // true
a = 123;
console.log(window.a); // 123
this.b = 456;
console.log(window.b); //456
console.log(b); //456
위 실행결과를 보면 알 수 있듯이, 전역 문맥에서는 세가지 모두 동일한 호출 방식이다.
this를 통한 접근,window를 통한 접근, 또는this나window를 붙이지 않은 일반적인 선언 및 호출
추가적으로, 'use strict' 키워드를 활용했을 때 활성화되는 strict mode와 관계없이 전역 문맥에서 this는 항상 window이다.
함수의 경우 전역 문맥과는 다르게 함수를 호출한 방법에 따라서 this가 달라진다.
this의 정의 자체가 '함수를 호출한 객체' 이기에, (호출한 방법이 달라진다 === 호출한 객체가 달라진다) 이다.
function func(){
return this;
}
//브라우저일 경우
console.log(func() === window); //true
//node.js일 경우
console.log(func() === global); // true
전역에서 함수();는 전역객체.함수();와 동일하기 때문에 전역문맥과 동일한 결과가 나온다.
반면, strict mode에서는 다른 결과가 나온다.
//'use strict'; 여기에 선언해도 동일한 결과
function f2() {
"use strict"; //strict mode
return this;
}
console.log(f2() === undefined); // true : 일반적인 함수 호출
console.log(window.f2() === window); // true : 전역 객체를 통해 호출
console.log(this === window); // true : 전역 맥락에서 직접 호출
strict mode의 함수 내의 this는 undefined가 되는데, 이는 strict mode의 특성을 살린 것이라고 볼 수 있다.
this의 암묵적인 전역 객체로의 바인딩을 막아 엄격하고 안전하게 관리한다.this가 아니라면 undefined로 설정하여 의도치 않은 전역 객체 참조를 방지한다.즉 한줄로 설명하면,
함수 맥락에서 strict mode는 this의 전역 객체로의 바인딩을 막아 객체를 엄격하게 관리한다.
const obj = {
a: 'hello',
b: function() {
console.log(this);
},
};
obj.b(); // {a: 'hello', b: ƒ}
해당 예시에서 b에서 호출한 this는 객체 obj가 된다.
b()는 객체 obj의 내부 메소드로서 호출되었기 때문이다.
"현재 함수(b)를 호출한 객체(obj)를 말한다." 라는 this의 정의가 보일 것이다.
객체에서 this를 활용하면 객체 내 다른 속성에 접근할 때 편리하게 활용 할 수 있다.
const obj = {
a: 'hello',
b: function() {
console.log(this.a); // obj.a와 동일
},
};
obj.b(); // hello
이와 같이 접근 할 수 있다.
const obj = {
a: 'hello',
b: function() {
console.log(this);
},
};
const c = obj.b;
c(); // window
만약 c에 obj.b를 저장한 뒤, c를 호출한다면 this는 무엇이 될까?
이때는 obj가 아니라 전역 객체인 window가 된다.
function() {console.log(this);}를 호출하는 대상이 obj가 아니라 전역 객체이기 때문이다.
this는 선언 지점에 의해 결정되는 것이 아닌 호출 지점에서 결정된다.
(렉시컬 스코핑 개념과 반대 개념이다.)
function func() {
console.log(this);
}
const obj = {
a: 'hello',
b: func,
innerObj: {
name: 'this is small obj',
d: func,
},
};
obj.c = func;
obj.b(); // {a: 'hello', innerObj: {…}, b: ƒ, c: ƒ}
obj.c(); // {a: 'hello', innerObj: {…}, b: ƒ, c: ƒ}
obj.innerObj.d(); // {name: 'this is small obj', func: ƒ}
func는 전역 맥락에서 선언 되었지만, 호출 지점은
b()c()d()이다.
각각 호출한 객체에 맞게 b와 c는 this가 obj를 반환하고, d는 this가 innerObj를 반환한다.
결국 이것도 this의 정의 "함수를 호출한 객체"에 부합하는 내용이다.
이렇게 함수의 호출 지점에 따라 this가 가리키는 객체는 달라진다.
bind
ES5에서 도입되어, 함수를 어떻게 호출했는지에 상관하지 않고 this값을 설정하도록 하는 것
ES6에서는 스스로의 this 바인딩을 제공하지 않는 화살표 함수가 도입되었다.
우리가 매일 사용하는 화살표 함수는 선언 당시의 렉시컬 스코프를 기억하여 선언 당시의 상위 객체를 가리키도록 한다.
const obj = {
a: 'hello',
b: function(){
const func = function() {
console.log(this); // window
console.log(this.a);
}
func();
},
}
obj.b(); // undefined
const obj = {
a: 'hello',
b: function(){
const func = () => {
console.log(this); // {a: 'hello', b: ƒ}
console.log(this.a);
}
func();
},
}
obj.b(); // hello
예제1과 예제2의 차이점은 단지 b에 정의된 함수 내의 func가 기본형에서 화살표 함수형으로 바뀌었다는 것이다.
예제 2에서는 func가 선언될 당시의 스코프를 기억하여 this는 obj를 호출한다.
function func(){
return console.log(this);
}
const funcBind = func.bind({name:'this is binding obj'});
const obj = {
innerObj: {
funcBind,
}
};
funcBind(); // {name: 'this is binding obj'}
obj.innerObj.funcBind(); // {name: 'this is binding obj'}
BindedFunc.bind(BindingObject) 의 형태로 바인딩을 진행하면,
해당 객체가 바인딩 된 새로운 함수를 반환한다.
함수가 어느 곳에서 호출되던지 상관 없이 this는 바인딩 된 객체를 참조한다.
const obj = {
a: 'hello',
b: function() {
console.log(this);
}.bind({'name':'bindinginging'}),
};
obj.b(); // {'name':'bindinginging'}
더 이상 b의 this는 obj를 참조하지 않는다.
이미 바인딩 된 함수를 다시 바인딩 할 수는 없다.
맨 처음에 바인딩 된 객체를 계속 참조하고, 이후 진행된 것은 무시된다.
function func(){
return console.log(this);
}
const funcBind = func.bind({name:'this is binding obj'});
const funcBindAgain = funcBind.bind({name: 'bind again'}); // 무시
funcBindAgain(); // {name:'this is binding obj'}