JavaScript의 this는 함수의 선언위치(Lexical Scope)와 관련있는 것이 아니라, 호출위치에 따라 동적으로 결정된다.
this는 전역객체를 기본으로 참조하고 있다.
function foo() {
console.log(this.a)
}
var a = 2
foo() // 2
함수 레퍼런스에 대한 객체가 존재할 때 암시적 바인딩 규칙에 따르며, 함수 호출 시 객체가 this에 바인딩 된다.
function foo() {
console.log(this.a)
}
var a = 'this is global'
var obj = {
a: 'this is local',
foo: foo
}
obj.foo() // this is local
객체내부에서 객체를 참조할 때 최상위, 최하위의 정보가 호출된다.
function foo() {
console.log(this.a)
}
var obj2 = {
a: 'this is obj2',
foo: foo
}
var obj1 = {
a: 'this is obj1',
obj2: obj2
}
obj1.obj2.foo() // this is obj2
❗ 암시적 소실
암시적으로 바인딩 된 경우 바인딩이 소실 되는 경우가 있다.
function foo() {
console.log(this.a)
}
var obj = {
a: 'this is local',
foo: foo
}
var a = 'this is global'
var bar = obj.foo
bar() // this is global
bar
는 obj
객체의 foo
함수를 참조하는 것 처럼 보이나 foo
함수를 직접 가리키고 있는 것이다.
function foo() {
console.log(this.a)
}
function callBack(fn) {
fn()
}
var obj = {
a: 'this is local',
foo: foo
}
var a = 'this is global'
callBack(obj.foo) // this is global
callBack
함수의 파라미터 fn
는 위 예시의 bar
변수와 같이 foo
함수를 직접 가리키는 또다른 참조값이다.
실제 많이 볼수 있는 예시는 아래와 같다.
function foo() {
console.log(this.a)
}
var obj = {
a: 'this is local',
foo: foo
}
var a = 'this is global'
setTimeout(obj.foo, 1000); // this is global
객체를 직접 지정하여 바인딩하는 방식이다.
var a = 'this is global'
function foo() {
console.log(this.a)
}
var obj = {
a: 'this is local'
}
foo.call(obj) // this is local
foo.call()
에서 obj
를 직접지정하여 this
에 바인딩 했으므로 foo.call()
의 this
는 항상 obj
이다.
❗ 하드 바인딩
암시적 바인딩의 문제였던 소실을 해결할 수 있는 방법이다.
var a = 'this is global'
function foo() {
console.log(this.a)
}
var obj = {
a: 'this is local'
}
var bar = function() {
foo.call(obj)
}
bar() // this is local
setTimeout(bar, 100) // this is local
함수 bar
는 내부에서 obj
로 바인딩된 함수 foo
를 호출하고 있다. 이 경우 bar
를 어떤식으로 호출하더라도 obj
로 바인딩된 foo
를 실행하게 된다.
바인딩할 객체를 동적으로 결정하고 싶은 경우 아래와 같은 함수를 사용할 수 있다.
function foo(something) {
console.log(this.a, something)
return this.a + something
}
function bind(fn, obj) {
return function() {
return fn.apply(obj, arguments)
}
}
var obj = {
a: 1
}
var bar = bind(foo, obj)
var b = bar(2) // 1 2
console.log(b) // 3
❗ 하드 바인딩은 자주 사용되는 패턴이여서 ES5 내장 함수로 Function.prototype.bind가 구현되어 있다.
❗ API 호출 컨택스트
JavaScript 내장 함수는 thisArgs
라는 선택적 인자를 제공한다. 이는 callback을 호출할 때 전달해 this
의 값으로 쓰인다.
arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])
function foo(el) {
console.log(el, this.id)
}
var obj = {
id: "wizard"
}
["harry", "ron", "hermione"].forEach(foo, obj)
// harry wizard
// ron wizard
// hermione wizard
function foo(a) {
this.a = a
}
var bar = new foo(2)
console.log(bar.a) // 2
새로 생성된 객체
bar
는this
에 바인딩 된다.
function foo() {
console.log(this.a)
}
var obj1 = {
a: "this is obj1",
foo: foo
}
var obj2 = {
a: "this is obj2",
foo: foo
}
obj1.foo() // this is obj1
obj2.foo() // this is obj2
obj1.foo.call(obj2) // this is obj2
obj2.foo.call(obj1) // this is obj1
명시적 바인딩이 암시적 바인딩보다 우선순위가 높다.
function foo(something) {
this.a = something
}
var obj1 = {
foo: foo
}
var obj2 = {}
obj1.foo('this is obj1')
console.log(obj1.a) // this is obj1
obj1.foo.call(obj2, 'this is obj2')
console.log(obj2.a) // this is obj2
var bar = new obj1.foo('this is bar')
console.log(bar.a) // this is bar
console.log(obj1.a) // this is obj1
new 바인딩이 암시적 바인딩 보다 우선 순위가 높다.
function foo(something) {
this.a = something
}
var obj1 = {}
var bar = foo.bind(obj1)
bar('this is obj1')
console.log(obj1.a) // this is obj1
var baz = new bar('this is baz')
console.log(obj1.a) // this is obj1
console.log(baz.a) // this is baz
new 바인딩이 명시적 바인딩 보다 우선 순위가 높다.
new
로 함수를 호출 했는지 → 맞으면 새로 생성된 객체가 this
다var bar = new foo()
call
, apply
, bind
를 이용해 함수를 호출 했는가? → 맞으면 명시적으로 지정된 객체가 this
다.var bar = foo.call(obj2)
this
다.var bar = obj1.foo()
this
는 기본값으로 설정된다.var bar = foo()
일반 함수와는 달리 화살표 함수의
this
언제나 상위 스코프의this
를 가리킨다. 이를Lexical this
라 한다.Lexical scope
와 유사하다.
function foo() {
return (a) => {
// this는 화살표함수의 상위 스코프인 foo 함수에 종속된다.
console.log(this.a)
}
}
var obj1 = {
a: 'this is obj1'
}
var obj2 = {
a: 'this is obj2'
}
var bar = foo.call(obj1)
bar.call(obj2) // this is obj1
function foo() {
// foo의 this를 따른다.
setTimeout(() => {
console.log(this.a)
}, 100)
}
var obj = {
a: 'this is obj'
}
foo.call(obj) // this is obj
❗ 예외사항
const person = {
name: 'Lee',
// 상위 스코프인 글로벌이 this로 지정된다.
sayHi: () => console.log(`I'm ${this.name}`)
};
person.sayHi(); // I'm undefined
화살표 함수는 객체의 프로퍼티로 지정하거나, prototype 프로퍼티로 지정할 경우 예측과 다르게 동작할 수 있으며, 생성자 함수로 사용할 수 없다.
YOU DON'T KNOW JS
https://poiemaweb.com/es6-arrow-function