JavaScript 의 this
JavaScript 의 this
1. 렉시컬 스코프
자바스크립트는 렉시컬 스코프를 사용한다.
렉시컬 스코프는 변수나 함수가 정의된 곳의 컨텍스트 를 사용한다.
자바스크립트는 함수 만이 자신의 스코프를 가질 수 있다.
let, const 키워드는 블록 스코프를 사용한다.
function foo1() {
var x = 1;
console.log(x); // 1
if (true) {
var x = 2;
console.log(x); // 2
}
console.log(x); // 2
}
foo();
function foo2() {
var x = 1;
if (true) {
(function () {
var x = 2;
console.log(x); // 2
})();
}
console.log(x); // 1
}
foo2();
2. 일반 함수의 this
함수가 어떻게 호출 되었는지에 따라 this 에 바인딩할 객체가 동적으로 결정된다.
2-1. 일반 함수로 호출
일반 함수의 this 는 기본적으로 window 객체이다.
this 를 생성자 혹은 객체의 메소드에서 사용하지 않는 경우 this 는 전역 객체가 된다.
const age1 = 100;
function foo() {
const age1 = 99;
bar(age1);
}
function bar(age) {
console.log(this.age); // 100
}
foo();
2-2. 오브젝트 메소드 형식으로 실행
오브젝트가 this 로 설정된다.
function foo() {
console.log(this.age2);
}
const age2 = 100;
const ken2 = {
age2: 36,
foo: foo
}
const wan2 = {
age2: 32,
foo: foo
}
ken2.foo(); // 36
wan2.foo(); // 32
2-3. call, apply, bind 를 통해 this 를 바꿀 수 있다.
- call, apply : 함수 내 this 를 call, apply 함수의 인자에 전달되는 값으로 바꾸고 함수를 호출한다.
- bind : 함수 내 this 를 바꾸기만 하고 함수를 호출하지는 않는다.
var age3 = 100;
function foo3() {
console.log(this.age3);
}
var ken3 = {
age3: 35,
log: foo3
}
kenLog();
foo3.call(ken3); // 35
foo3.apply(ken3); // 35
2-4. new 로 생성된 객체
new 로 생성된 객체가 this 로 할당된다.
function Person4() {
this.name = 'ken';
}
var ken4 = new Person4();
console.log(ken4); // Person4 { name: 'ken' }
3. 화살표 함수의 this
화살표 함수의 this 는 렉스컬 스코프로 상위 스코프의 this 를 가리킨다.
화살표 함수는 call, apply, bind 메소드를 사용해 this 를 변경할 수 없다.
3-1. 생성자 함수의 this
function Person1() {
// Person1 생성자는 this 를 자신의 인스턴스로 정의
this.age = 0;
// 일반 함수의 this 는 window 객체
setInterval(function growUp() {
// 비엄격 모드에서 growUp() 함수의 this 는 전역 객체로 정의하고
// 이는 Person1 생성자에 정의된 this 와 다름
console.log(this.age++);
}, 1000);)
}
function Person2() {
const that = this;
that.age = 0;
setInterval(function growUp() {
// 콜백은 that 변수를 참조하고 이 것은 값이 기대한 객체이다.
console.log(that.age++);
}, 1000);
}
function Person3(0 {
this.age = 0;
setInterval(() => {
console.log(this.age++);
}, 1000);
});
const p1 = new Person1();
const p2 = new Person2();
const p3 = new Person3();
3-2. prototype 의 this
function Prefixer(prefix) {
this.prefix = prefix;
}
Prefixer.prototype.prefixArray1 = function (arr) {
return arr.map(function (x) {
// prefixArray1 함수를 호출하는 곳의 this 를 가져온다.
// 아래 bind 함수가 주석이면 this 는 window 객체가 된다.
return this.prefix + ' ' + x;
})
// this: Prefixer 생성자 함수의 인스턴스
// .bind(this);
}
Prefixer.prototype.prefixArray2 = function (arr) {
// this 는 상위 스코프인 prefixArray2 메소드 내의 this 를 가리킨다.
console.log('prefixArray2 this', this);
return arr.map(x => `${this.prefix} ${x}`);
}
const pre = new Prefixer('Hi');
console.log(pre.prefixArray1(['Lee', 'Kim'])); // ["undefined Lee", "undefined Kim"]
console.log(pre.prefixArray2(['Lee', 'Kim'])); // ["Hi Lee", "Hi Kim"]
3-3. 화살표 함수로 메소드 정의
화살표 함수로 메서드를 정의하는 것은 피해야 한다.
화살표 함수 내부의 this 는 메서드를 소유한 객체를 가리키지 않고, 상위 컨텍스트인 window 를 가리킨다.
const person3 = {
name3: 'Lee',
sayHi: () => console.log(`Hi ${this.name3}`)
}
const person4 = {
name4: 'Lee',
sayHi() {
console.log(`Hi ${this.name4}`);
}
}
person3.sayHi(); // Hi undefined
person4.sayHi(); // Hi Lee
3-4. 화살표 함수로 prototype 을 정의
화살표 함수로 prototype 에 할당하는 경우 this 는 상위 컨텍스트인 window 객체를 가리킨다.
function Person5() {
this.name5 = 'Lee';
}
Person5.prototype.sayHi = () => console.log(`Hi ${this.name4}`);
const p5 = new Person5();
p5.sayHi() // Hi undefined
3-5. 화살표 함수를 생성자 함수로 사용
화살표 함수는 생성자 함수로 사용할 수 없다.
생성자 함수는 prototype 프로퍼티를 가지고 prototype 프로퍼티가 가리키는 prototype 객체의 constructor 를 사용한다.
하지만 화살표 함수는 prototype 프로퍼티를 가지고 있지 않다.
const Foo4 = () => {};
console.log(Foo4.hasOwnProperty('prototype')); // false
const foo4 = new Foo4(); // TypeError: Foo is not a constructor