오늘은 자바스크립트의
this
에 대하여 정리하기로 했다. 어? 분명히 정리했던 것 같은데 왜 또 정리하지? -> 오늘 면접스터디를 했는데 이 파트에 대하여 정리하기로 했기 때문이다. 그래서 저번보다는 조금 더 자세히 적어보려고 한다.
함수를 호출 시, 우리가 직접 전달하는 arguments
들 외에도 암묵적으로 this
를 전달하게 된다. 아마 일반적인 다른 언어에서는 this(self)
는 인스턴스 자신을 가리키는 참조 변수일 것이다. 그러나 자바스크립트에서 this
는 다른 언어와는 달리 호출 주체가 누구인지, 함수의 호출 방식에 따라 달라진다.
함수를 호출하는 방법은 총 4가지가 있으며, 이 호출방식에 따라 this
값은 달라진다.
1. 일반적인 함수 키워드 function
으로 this
호출
2. 생성자 함수로서 호출
3. 메소드 호출
4. call/apply/bind
함수로 호출
일반적인 함수에서 this
호출 시, 기본적으로 전역 객체를 가리킨다.
function hello() {
console.log(this); // window
}
자바스크립트는 생성자 함수가 따로 있는게 아니고, 그냥 new
키워드를 붙여서 함수를 호출한다면, 그 함수는 생성자 함수가된다. new
키워드를 통해 함수를 호출할 경우, this
는, 호출한 인스턴스를 가리킨다.
function Person(name) {
this.name = name;
console.log(this);
this.greet = () => console.log(`hello, ${this.name}!`);
}
const peter = new Person('peter'); // this는 instance를 가리킴
peter.greet(); // hello, peter!
객체 리터럴 내부에서 함수를 호출한다면, this
는 객체를 가리킨다.
function foo() {
console.log(this);
}
foo(); // window
const bar = { foo: foo };
bar.foo(); // bar
call/apply/bind
는 this
를 명시적으로 바인딩할 수 있다.
var value = 1;
var obj = {
value: 100,
foo: function() {
console.log("foo's this: ", this); // obj
console.log("foo's this.value: ", this.value); // 100
function bar(a, b) {
console.log("bar's this: ", this); // obj
console.log("bar's this.value: ", this.value); // 100
console.log("bar's arguments: ", arguments);
}
bar(); // window
// 바인딩 함수로 바인딩할 경우, 인스턴스 또는 객체의 this를 따르게 할 수 있다.
bar.apply(obj, [1, 2]);
bar.call(obj, 1, 2);
bar.bind(obj)(1, 2);
}
};
obj.foo();
this
호출Arrow Function은 일반적인 함수보다 좀 더 간결하게 사용할 수 있는 표현식이다. arrow function과 일반 함수와의 가장 큰 차이점은 this
이다. arrow function은 this
를 바인딩하지 않는다. 그렇기 때문에 무조건 상위 스코프의 this
를 따른다. 또한, call/apply/bind
함수로 this
바인딩 역시 불가능하다.
function Prefixer(prefix) {
this.prefix = prefix;
}
Prefixer.prototype.prefixArray = function (arr) {
// (A)
return arr.map(function (x) {
return this.prefix + ' ' + x; // (B)
});
};
var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim']));
B의 this
는 전역 객체 window
를 가리킨다. 기본적으로 일반 함수의 this
는 전역 객체를 가리키기 때문이다. (메소드로 호출된 첫번째 함수의 this
만 인스턴스를 가리킨다.)
따라서 내가 의도한 로직을 실행하기 위해서는 call/apply/bind
로 this
를 바인딩하여야 한다.
Prefixer.prototype.prefixArray = function (arr) {
// (A)
return arr.map(function (x) {
return this.prefix + ' ' + x; // (B)
}.bind(this)); // 이런식으로
};
하지만 ES6에서 새롭게 추가된 화살표 함수는, 무조건 상위 스코프의 this
를 따라가므로 바인딩할 필요가 없다.
// arrow function
Prefixer.prototype.prefixArray = function (arr) {
return arr.map((x) => this.prefix + ' ' + x); // 이런식으로
};
this
ES5에서 새롭게 추가된 엄격모드는, 느슨한 체킹을 했던 자바스크립트에 엄격한 타입 체킹과, 조용이 무시되던 에러들을 반환하는 모드이다. this
를 명시하지 않았다면, 전역 객체가 아닌 undefined
를 반환한다.
function f() {
return this;
}
console.log(f()); // undefined
console.log(f().call(2)); // 2
console.log(f().apply(null)); // null
console.log(f().bind('string'); // string