[모던 자바스크립트 Deep Dive] 22장 - this
😉 this 키워드
자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수
this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.
- this가 가르키는 값, 즉 this 바인딩은 함수 호출 방식에 의해 동적으로 생성된다.
- this는 어디에서든 참조가 가능하다.
- 일반적으로 객체의 메서드내부 또는 생성자 함수 내부에서만 의미가 있다.
- strict mode가 적용된 일반 함수 내부의 this에는 undefined가 바인딩된다.(일반함수 내부에서는 this를 사용할 필요가 없기 때문)
😉 함수 호출 방식과 this 바인딩
this 바인딩은 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.
렉시컬 스코프와 this 바인딩은 결정 시기가 다르다.
함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위 스코프를 결정한다.
하지만 this 바인딩은 함수 호출 시점에 결정된다.
주의할 것은 동일한 함수도 다양한 방식으로 호출할 수 있다는 것이다. 다음은 함수 호출 방식이다.
일반 함수 호출
- this는 전역 객체를 가리킨다.
메서드 호출
- this는 메서드를 호출한 객체를 가리킨다.
생성자 함수 호출
- this는 생성자 함수가 생성한 인스턴스를 가리킨다.
Function.prototype.apply/call/bind 메서드에 의한 간접 호출
- this는 인수에 의해 결정된다.
🐱🐉 일반 함수 호출
기본적으로 this에는 전역객체(대게는 window)가 바인딩 된다.
function foo() {
console.log("foo's this :", this); //window
function bar() {
console.log("bar's this: ", this); //window
}
bar();
}
foo();
주의) 어떤 함수라도 일반 함수로 호출되면 this에 전역객체가 바인딩된다.
- 메서드 내에 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는 전역 객체가 바인딩 된다.
var value = 1;
const obj = {
value : 100,
function foo() {
console.log("foo's this :", this); // {value : 100, foo:f}
function bar() {
console.log("bar's this: ", this); // window
}
bar(); // 일반 함수로 호출됨
// 따라서 메서드 내에 중첩 함수도 일반함수처럼 호출되면
// 내부의 this는 전역개체에 바인딩 된다.
}
}
obj.foo();
- 콜백 함수가 일반함수로 호출되면 콜백 함수 내부의 this에도 전역객체가 바인딩된다.(콜백함수로 호출되지 않은 경우로 이해된다.)
var vaule = 1;
const obj = {
value: 100,
foo() {
console.log("foo is this : ", this); // {value: 100, foo:f}
// 콜백함수 내부의 this에는 전역 객체가 바인딩 된다.
SetTimeout(function () {
console.log("callback's this : ", this); // window
console.log("callback's this.value :", this.value); // 1
}, 100);
},
};
obj.foo();
중첩함수나 콜백 함수의 this바인딩을 메서드의 this 바인딩과 일치시키기 위한 방법
- this바인딩 변수를 할당한다.
var value = 1;
const obj = {
value: 100,
foo() {
// this바인딩을 변수 that에 할당한다.
const that = this;
// 콜백 함수 내부에서 this대신 that을 참조한다.
setTimeout(function () {
console.log(that.value); // 100
}, 100);
},
};
obj.foo();
- Function.prototype.apply/call/bind 사용
var vaule = 1;
const obj = {
value: 100,
foo() {
// 콜백 함수에 명시적으로 this를 바인딩한다.
setTimeout(
function () {
console.log(this.value); // 100
}.bind(this),
100
);
},
};
obj.foo();
- 화살표 함수를 사용해 this바인딩을 일치시킨다.
var value = 1;
const obj = {
value: 100,
foo() {
// 화살표 함수 내부의 this는 상위 스코프의 this를 가르킨다.
setTimeout(() => console.log(this.value), 100); // 100
},
};
obj.foo();
🐱🐉 메서드 호출
메서드를 호출할 때 메서드 이름앞의 마침표(.)연산자 앞에 기술한 객체가 바인딩된다.
즉, 호출한 객체에 바인딩 된다.
프로토타입 메서드 내부에서 사용된 this도 호출한 객체에 바인딩 된다.
- 메서드는 프로퍼티에 바인딩된 함수이다.
즉, 객체에 프로퍼티가 가리키는 함수 객체는 객체에 포함된 것이 아닌 독립적으로 존재하는 별도의 객체임으로 다른 객체에 프로퍼티로 할당할 수도 있고, 일반 변수에 할당도 가능하며 일반 함수로도 호출이 가능하다.
const person = {
name: "Lee",
getName( {
//메서드 getName을 호출한 person 객체와 바인딩 된다.
return. this.name;
})
}
console.log(person.getName()) // Lee
const anotherPerson = {
name: "kim",
};
//getName 메서드를 anotherPerson 객체의 메서드로 할당
anotherPerson.getName = person.getName;
//getName 메서드를 호출한 객체는 anotherPerson이다.
console.log(anotherPerson.getName()); // kim
//getName 메서드를 변수에 할당
const getName = person.getName;
//getName 메서드를 일반함수로 호출
console.log(getName()); // ' '
//일반 함수로 호출된 getName 함수 내부의this는 브라우저의 환경인 window.name다.
//windw.name의 기본값인 ' '이 나온다.
//strict 모드에서는 undefined가 출력된다.
function Person(name) {
this.name = name;
}
Person.prototype.getName = function () {
return this.name;
};
const me = new Person("Lee");
console.log(me.getName()); // Lee
Person.prototype.name = "Kim";
console.log(Person.prototype.getName()); // Kim
🐱🐉 생성자 함수 호출
생성자 함수 내부의 this에는 생성자 함수(미래에)생성할 인스턴스가 바인딩된다.
//생성자 함수
function Circle(radius){
//생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
this.radius = radius;
this.getDiameter = function(){
return 2 \* this.radius;
};
}
//반지름이 5인 Circle 객체를 생성
const circle1 = new Circle(5);
//반지름이 10인 Circle 객체를 생성
const circle2 = new Circle(10);
console.log(circle1.getDiameter()); //10
console.log(circle2.getDiameter()): //20
- 생성자 함수 정의 후 new연산자와 함께 호출하면 생성자 함수로 동작한다.
- new연산자 없이 호출하면 일반함수로 동작한다.
🐱🐉 Function.prototype.apply/call/bind 메서드에 의한 간접 호출
Function.prototype.apply / call 메서드
this로 사용할 객체와 인수 리스트를 인수로 전달받아 함수를 호출한다.
-
apply/call 메서드는 함수를 호출하면서 첫번째 인수로 전달한 특정 객체를 호출한 함수의 this를 바인딩한다.
- apply : 주어진 this바인딩과 인수 리스트를 배열을 사용
- call : 주어진 this바인딩과 ,로 구분된 인수 리스트르르 사용하여 함수를 호출한다.
function getThisBinding(){
console.log(arguments);
return this;
}
//this로 사용할 객체
const thisArg = {a : 1};
//getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBing 함수의 this에 바인딩된다.
//apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다.
console.log(getThisBinding.apply(thisAr[1,2,3]);
//Argumetns(3)
//{a : 1}
//call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다.
console.log(getThisBinding.call(thisAr,1,2,3));
//Arguments(3)
//{a : 1}
Function.prototype.bind 메서드
첫번째 인수로 전달한 값으로 this바인딩이 교체된 함수를 새롭게 생성해 반환하다.
- bind 메서드는 함수를 호출하지 않아 명시적으로 호출해야 한다.
function getThisBindgs() {
return this;
}
//this로 사용할 객체
const thisArg = { a: 1 };
//bind메서드 사용
console.log(getThisBinding.bind(thisArg)); //getThisBinding
//bind메서드는 함수를 호출하지 않으므로 명식적으로 호출해줘야 한다.
console.log(getThisBinding.bind(thisArg)());
{
a: 1;
}
- 중첩 함수 또는 콜백 함수의 this가 불일치하는 문제를 해결하기 위해 유용하게 사용된다.
const person = {
name = "Lee",
foo(callback){
//bind메서드로 callback 함수 내부의 this 바인딩을 전달
setTimeout(callback.bind(this),100);
}
};
person.foo(function(){
console.log(`Hi!my name is ${this.name}`);
});
외부 함수 person.foo()의 콜백 함수와 내부의 person.foo()의 this의 객체는 다른 서로 다른 객체로 바인딩 되어 있다. 내부는 메서드안의 this임으로 메서드를 호출하는 객체인 person에 바인딩 되어 있으고, 외부 함수의 콜백 함수는 걍 person객체 안에 있는 foo()함수를 똑 떼어내서 독립적으로 사용했다고 생각하면 this에는 전역객체(window)가 바인딩 된다.
this를 일치 시켜야 원하는 값을 얻을 수 있기에, bind메서드를 이용해 내부 메서드의 this의 값을 그대로 사용할 수 있도록 하는 것이다.
Leave a comment