this
JS에서 함수의 this
는 실행 컨텍스트에 따라 다양한 값을 가진다.
this
가 쓰이는 함수를 어떤 방식으로 실행하는지에 따라 역할이 구분된다.
이를 바인딩이라하며, 함수 호출 방식에 따라 this
에 바인딩할 객체가 동적으로 결정된다.
함수 호출 방식은 4가지가 있다.
글로벌 영역에 선언한 함수는 전역객체의 프로퍼티로 접근할 수 있는 전역변수의 메소드가 된다.
전역객체는 모든 객체의 최상위 객체이며, 브라우저에선 window
, 서버에선 global
이 된다.
function foo() {
console.log(this); // window
function bar() {
console.log(this); // window
}
bar();
}
foo();
window.foo();
즉, 글로벌 영역에 선언한 함수는 전역객체(window, global
)의 프로퍼티로 접근이 가능한 메소드가 된다.
글로벌 영역에 선언한 foo
함수는 window
의 메소드가 되어 window.foo()
처럼 사용할 수 있다.
foo
함수의 내부함수인 bar
의 this
도 전역객체에 바인딩된다.
내부함수의 this
는 일반 함수, 메소드, 콜백 함수 관계없이 전역객체를 바인딩한다.
함수가 객체의 프로퍼티 값이면 메소드로 호출된다. 메소드 내부의 this
는 해당 메소드를 소유한 객체에 바인딩된다.
var foo = {
name: 'Lee',
sayName: function () {
console.log(this.name);
},
};
var bar = {
name: 'Kim',
};
bar.sayName = foo.sayName;
foo.sayName();
bar.sayName();
위 컨텍스트의 실행 결과는 Lee, Kim
이다.
sayName
함수는 foo
객체의 프로퍼티 값이므로 메소드로 호출된다. 메소드 내부의 this
는 해당 메소드를 소유한 객체, 즉 foo
객체에 바인딩된다.
그러므로 foo.sayName();
의 결과는 Lee
가 된다.
다음으로 bar
객체에 sayName
이라는 키를 생성했고, 그 값에 함수를 할당했다. bar
함수의 sayName
은 함수이므로, 메소드로 호출되며 sayName
메소드는 bar
객체가 소유하고 있다.
그러므로 bar.sayName();
의 결과는 Kim
이 된다.
JS의 생성자 함수는 객체를 생성하는 역할을 한다. 기존 함수에 new
연산자를 붙여 호출하면 그 함수는 생성자 함수로 동작한다.
function FOO(name) {
this.name = name;
}
var me = new FOO('Kim');
console.log(me); // FOO {name: 'Kim'}
new
연산자로 생성자 함수를 호출하면 아래와 같은 순서로 동작한다.
생성자 함수의 코드가 실행되기 전, 그러니까 위에서 this.name = name;
이 실행되기 전에 빈 객체가 생성된다.
생성자 함수가 이 빈 객체를 새로 생성하고, 생성자 함수 내에서 사용되는 this
는 빈 객체에 바인딩된다.
생성된 빈 객체에 this
를 사용하여 동적으로 프로퍼티나 메소드를 생성한다. 위에선 name
이라는 프로퍼티를 생성했다.
이렇게 생성된 프로퍼티는 새로 생성된 객체 me
에 추가된다.
생성된 함수를 반환한다. 위에선 생성자 함수에 반환문(return
)이 없으므로, this
에 바인딩된 새로 생성한 객체가 반환되어 FOO {name: 'Kim'}
가 결과로 나오게 된다.
생성자 함수에 반환문이 있는 경우, this
를 반환하지 않고 반환하므로, 생성자 함수에선 반환문을 명시적으로 사용하지 않는다.
new
연산자를 붙이지 않으면 오류가 발생할 수 있다.
new
연산자를 붙이면 위 순서와 같이 생성자 함수가 생성한 빈 객체에 this
가 바인딩된다.
그러나 붙이지 않으면 this
는 전역객체에 바인딩된다.
apply
/ call
/ bind
호출this
를 특정 객체에 명시적으로 바인딩할 수 있다. 모든 함수 객체의 프로토 타입 객체인 Function.prototype
의 메소드인 apply
, call
, bind
로 수행할 수 있다.
apply
apply()
는 this
를 특정 객체에 바인딩할 함수를 호출한다.
var Person = function (name) {
this.name = name;
};
var foo = {};
Person.apply(foo, ['John']);
console.log(foo); // {name: 'John'}
apply
메소드의 첫 번째 인자로 빈 객체 foo
, 두번째 인자로 Person
함수에 전달 할 매개변수의 배열을 할당한다.
이렇게 되면 Person
함수의 this
는 foo
객체에 바인딩된다. foo
객체에 name
프로퍼티가 없으므로, 동적으로 생성된다. 이후 John
이라는 값이 할당된다.
call
call
은 apply
와 기능은 같다. 차이점은 두번째 인자에서 배열 형태를 각각 하나의 인자로 전달하는 것이다.
Person.apply(foo, [1, 2, 3]);
Person.call(foo, 1, 2, 3);
bind
bind
는 인자로 전달한 this
가 바인딩된 새로운 함수를 반환한다. apply
와 call
은 바로 함수를 실행하지만, bind
는 새로운 함수를 반환하므로 다시 호출해야한다.
var Person = function (name) {
this.name = name;
};
var foo = {};
Person.bind(foo, 'John')();
console.log(foo); // {name: 'John'}