자바스크립트 this 를 정리합니다.
자바스크립트는 함수를 호출할때 파라미터로 전달하는 값 외에 arguments 와 this 를 추가로 전달받습니다.
1 | function square(number) { |
자바의 경우라면 this 는 객체 자신을 참조하겠지만, 자바스크립트의 경우는 조금 다릅니다.
함수 호출 방식과 this
자바스크립트의 경우 함수 호출 방식에 의해 this 에 바인딩할 객체가 동적으로 지정됩니다.
호출 방식은 아래와 같습니다.
- function 호출
- method 호출
- 생성자 함수 호출
- apply / call / bind
- ES6 arrow function
1 | var foo = function () { |
function 호출
기본적으로 this 는 전역객체(window) 에 바인딩 됩니다. 전역 함수는 물론이고 내부함수의 경우에도 this 는 외부함수가 아닌 전역객체로 에 바인딩 됩니다.
1 | function foo() { |
method 내부의 function 일 경우에도 this 는 전역객체에 바인됭 됩니다.
1 | var value = 1; |
callback function 의 경우에도 this 는 전역객체에 바인딩 됩니다.
1 | var value = 1; |
결론은,
function 은 어디에 선언되었든 관계없이 this 가 전역객체에 바인됭 됩니다.
그러나, this 가 전역객체 참조를 회피하는 방법이 있습니다.
1 | var value = 1; |
이 방법 외에도 apply, call, bind 메소드를 통해 참조를 변경할 수 있습니다.
1 | var value = 1; |
method 호출
함수가 객체의 프로퍼티로서 존재한다면 이는 메소드로 호출되게 됩니다. 이때 메소드 내부의 this 는 해당 메소드를 호출한 객체가 됩니다.
1 | var obj1 = { |
프로토타입 객체도 메소드를 가질 수 있습니다. 프로토타입 객체 메소드 내부에서 사용된 this 도 일반 메소드 방식과 마찬가지로 해당 메소드를 호출한 객체에 바인딩 됩니다.
1 | function Person(name) { |
생성자 함수 호출
function 에 new 연산자를 붙여 호출하면 해당 함수는 생성자 함수로 동작하게 됩니다.
1 | // 함수 |
new 연산자와 함께 생성자 함수를 호출하면 this 바인딩이 메소드나 함수 호출때와 다르게 동작합니다.
생성자 함수 동작 방식
new 연산자와 함께 생성한 함수를 호출하면 다음과 같은 순으로 동작하게 됩니다.
빈 객체 생성 및 this 바인딩
생성자 함수의 코드가 실행되기 전 빈 객체가 생성된다. 이 빈 객체가 생성자 함수가 새로 생성하는 객체이다. 이후 생성자 함수 내에서 사용되는this는 이 빈 객체를 가리킨다. 그리고 생성된 빈 객체는 생성자 함수의prototype프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다.this를 통한 프로퍼티 생성
생성된 빈 객체에this를 사용하여 동적으로 프로퍼티나 메소드를 생성할 수 있다.this는 새로 생성된 객체를 가리키므로this를 통해 생성한 프로퍼티와 메소드는 새로 생성된 객체에 추가된다.생성된 객체 반환
반환문이 없는 경우,this에 바인딩된 새로 생성한 객체가 반환된다. 명시적으로this를 반환하여도 결과는 같다.
반환문이this가 아닌 다른 객체를 명시적으로 반환하는 경우,this가 아닌 해당 객체가 반환된다. 이때this를 반환하지 않은 함수는 생성자 함수로서의 역할을 수행하지 못한다. 따라서 생성자 함수는 반환문을 명시적으로 사용하지 않는다.
1 | function Person(name) { |
객체 리터럴 방식과 생성자 함수 방식의 차이
1 | // 객체 리터럴 방식 |
- 리터럴 방식의 경우, 생성된 객체의 프로토타입 객체는
Object.prototype입니다. - 생성자 함수 방식의 경우, 생성된 객체의 프로토타입 객체는
Person.prototype입니다.
ES6 의 class
1 | class Person { |
동일한 new 연산자를 사용하였이며 class 를 사용한다고 하여 크게 다르지 않습니다.
apply / call / bind
this 에 바인딩될 객체는 함수 호출 방식에 의해 결정됩니다. 이는 자바스크립트 엔진이 암묵적으로 수행하는것이지만, 개발자가 직접 지정하는 방법을 제공하기도 합니다.
Function.prototype.applyFunction.prototype.call
이 메소드들은 모두 함수 객체의 프로토타입 객체의 메소드 입니다.
apply
1 | func.apply(thisArg, [argsArray]) |
apply 메소드를 호출하는 주체는 함수입니다.
1 | var Person = function (name) { |
빈 객체 foo 를 apply() 메소드의 첫번째 매개변수에, argument 의 배열을 두번째 매개변수에 전달하여 Person 함수를 호출했습니다.
Person 함수의 this 는 foo 객체에 바인딩 되었습니다.this.name 프로퍼티에 매개변수 name 을 대입하려 하지만, foo 객체에는 name 프로퍼티가 없으므로 name 프로퍼티가 동적으로 추가되고 값이 할당됩니다.
call
1 | Person.apply(foo, [1, 2, 3]); |
apply 와 call 메소드는 기능은 같지만 두번쨰 인자를 배열형태와 각각 넘기는것에 차이가 있을뿐입니다.
bind
ES5에 추가된 Function.prototype.bind 를 이용할 수 도 있습니다.
bind 는 함수에 인자로 전달한 this 가 바인딩되는 새로운 함수를 리턴합니다.
bind 는 apply, call 과 달리 함수를 바로 실행하지 않기때문에 명시적으로 함수를 호출할 필요가 있습니다.
1 | function Person(name) { |
ES6 Arrow function
1 | function Foo(bars) { |
callback function 의 경우에도 this 는 전역객체에 바인딩 되는것을 위에서도 언급했습니다.
es6 에 추가된 문법인 arrow 를 사용하게 될 경우 this 는 new 키워드로 생성한 객체에 바인딩 됩니다.
1 | function Foo(bars) { |
ES6 문법이므로 babel 을 사용하여 트랜스해보면 아래와 같은 결과를 볼 수 있습니다.
https://babeljs.io/
1 | function Foo(bars) { |