누군가 나에게 자바스크립트에서 가장 헷갈리는 키워드를 고르라고 한다면 나는 this를 이야기할 것 같다.
this 키워드는 모든 함수 스코프 내에 자동으로 설정되는 특수한 식별자로서
숙련된 개발자들 조차도 정확히 무엇을 가르키고 있는지 대답하기가 껄끄러울 것이라고 생각된다.
나 또한 그렇고 그래서 this에 관하여 포스팅하고자 한다.
this란 말이죠오오옹 - ?
this는 기본적으로 실행 컨텍스트가 생성될 때 결정된다.
즉 this는 함수가 실행될 때 결정된다고 말할 수 있는 것이며 함수가 호출되는 시점에 동적으로 정해지는 것이다.
그럼 어떻게해야 this를 나의 의도대로 잘 전달해줄 수 있을까?
this의 바인딩은 크게 4가지로 나눌 수 있다.
function foo(){
console.log( this.value );
}
var value = 10;
foo(); // 10
var 키워드를 통해서 value라는 변수를 전역으로 선언하였다.
이 때 전역 스코프에 변수명과 같은 이름의 전역 프로퍼티가 생성된다.
그리고 foo() 함수 호출시 this.value는 전역 객체 value이며 기본 바인딩이 적용되어
this는 전역 객체( 브라우저 환경에서는 window , Node 환경에서는 global )를 참조한다.
기본 바인딩 규칙이 적용된 것을 알 수 있는 점은 foo() 함수의 호출부를 보면 된다.
foo() 함수는 평범하게 함수 레퍼런스로 호출되었기 때문에 기본 바인딩이 적용되었다.
엄격모드 (strict mode)에서는 전역 객체가 기본 바인딩 대상에서 제외된다고 하며 this는 undefined가 된다고 한다.
따라서 엄격모드에서 전역 객체에 바인딩해주고 싶다면 window.foo() 를 해주도록 하자.
function foo(){
console.log( this.value )
}
var obj = {
value : 10,
foo:foo
}
obj.foo(); // 10
foo() 함수를 obj에서 프로퍼티로 참조하고 있다.
함수가 객체의 프로퍼티 값이면 메소드로서 호출이 된다.
이 때 메소드 내부의 this는 해당 메소드를 소유한 객체 ( foo()를 호출한 obj )에 바인딩된다.
즉 함수 레퍼런스에 대한 콘텍스트 객체가 존재할 때 암시적 바인딩이 되는 것이다.
function foo(){
console.log(this.name)
}
var obj = {
name : "dongdong",
foo:foo
}
var foofoo = obj.foo;
var name = "Kim";
foofoo();
암시적 바인딩에 의해서 dongdong이 출력될 것 같다.
foofoo는 obj의 foo를 참조하는 변수처럼 보이지만 사실은 foo를 직접 가르키는 또 다른 레퍼런스이며
호출부에서 foofoo()를 호출하기 때문에 Kim이 출력이 된다.
위 예제코드는 보기 쉽고 직관성있게 짠 코드라 금방 알아차릴 수 있겠으나
실제로 사용되는 코드에서는 이보다 더욱 this의 행방을 찾기 어려운 경우가 있을 수 있다.
따라서 this를 바인딩하겠다는 의사를 코드에 나타낼 방법이 바로 call() / bind() / apply() 메서드를 이용한 명시적 바인딩이다.
this에 바인딩 할 객체를 첫째 인자로 받아 함수 호출 시 이 객체를 this로 세팅한다.
this를 지정한 객체로 직접 바인딩 하므로 이를 명시적 바인딩이라고 한다.
function foo(){
console.log(this.value);
}
var obj = {
value : 2
}
foo.call(obj) // 2
foo.call()에서 명시적으로 바인딩하여 함수를 호출하므로 this는 obj가 되는 것을 보장받을 수 있다.
// 생성자 함수
function Person(name) {
// 생성자 함수 코드 실행 전 빈 객체를 생성하고 this를 바인딩한다.
this.name = name;
// this를 통하여 프로퍼티를 생성한다.
// 생성된 객체를 반환한다.
}
var me = new Person('Lee');
console.log(me); // Person {name: "Lee"}
// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person('Kim');
console.log(you); // undefined
기존 함수에 new 연산자를 붙여서 호출하게 되면 해당 함수는 객체를 생성하는 역할을 하는 생성자 함수로 동작하게 된다.
일반적으로 생성자 함수명은 첫 번째 문자를 대문자로 사용하여 혼란을 방지한다.
ex) new Foo();
생성자 함수를 호출하게 되면 this의 바인딩이 다르게 동작하게 되며 아래와 같이 동작한다고 한다.
일반 함수를 호출하면 this는 전역객체에 바인딩되지만 new 연산자와 함께 생성자 함수를 호출하면 this는 생성자 함수가
암묵적으로 생성한 빈 객체에 바인딩된다.