this
자바스크립트에서 함수 호출 방식에 의해 this에 바인딩할 어떤 객체가 동적으로 결정된다.
함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니고,
함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.
this를 이용하는 함수를 4가지 방식 중에서 어떤 방식으로 실행하느냐에 따라 this의 값이 결정된다는 뜻이다.
함수의 호출 방식
1. 함수 호출
2. 메서드 호출
3. 생성자 함수 호출
4. apply / call / bind 호출
const tell = function () {
console.dir(this);
};
// 1. 함수 호출
tell(); // window
// 2. 메소드 호출
const person = { say: tell };
person.say(); // object
// 3. 생성자 함수 호출
const instance = new tell(); // tell이라는 object
// 4. apply/call/bind 호출
const user = { name: 'taenam' };
tell.call(user); // object
tell.apply(user); // object
tell.bind(user)(); // object
1. 함수 호출
전역 객체는 모든 객체의 최상위 객체를 의미하며,
console.log(this === window) // true
Broswer에서는 window 객체,
console.log(this === module.exports); // true
console.log(this === exports); // true
console.log(module.exports === exports);// true
function a() {
console.log(this === global);
}
const b = () => {
console.log(this === exports);
};
a(); // true
b(); // true
Node에서의 전역 환경에서의 this는 module.exports, exports이고, 함수 선언문 안의 this는 global이다.
기본적으로 this는 전역 객체(Global object)에 바인딩된다.
전역 함수, 내부 함수의 경우에도 this는 외부 함수가 아닌 전역 객체에 바인딩된다.
var age = 1;
const obj = {
age: 29,
foo: function() {
const person = this;
console.log(this); // obj
console.log(this.age); // 29
function bar() {
console.log(this); // window
console.log(this.age); // 1
console.log(person); // obj
console.log(person.age); // 29
}
bar();
}
};
obj.foo();
2. 메서드 호출
함수가 객체의 프로퍼티 값이면 메서드로서 호출된다.
이때, 메소드 내부의 this는 해당 메서드를 소유한 객체, 즉 해당 메소드를 호출한 객체에 바인딩된다.
const person1 = {
name: 'Lee',
sayName: function() {
console.log(this.name);
}
}
const person2 = {
name: 'Kim'
}
person2.sayName = person1.sayName;
person1.sayName(); // Lee
person2.sayName(); // Kim
3. 생성자 함수 호출
자바스크립트의 생성자 함수는 말 그대로 객체를 생성하는 역할을 한다.
기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.
// 생성자 함수
function Person(name) {
this.name = name;
this.area = 'seoul';
}
const me = new Person('Taenam');
console.log(me); // Person {name: "Taenam", area: "seoul"}
console.log(me.area); // seoul
new 바인딩의 동작 순서
1. new 키워드와 함께 생성자 함수로 사용되는 즉시, 함수 내부의 this는 빈 객체가 된다.
2. 새로 생성된 객체의 Prototype 체인이 호출 함수의 프로토타입과 연결됨
3. 1에서 생성된 객체를 this context 객체로 사용(명시적으로)하여 함수가 실행됨
4. 이 함수가 객체를 반환하지 않는 한 1에서 생성된 객체가 반환됨
4. 명시적 바인딩, apply/call/bind 호출
this의 역할을 우리가 직접 명확하게 지정해준다는 뜻이다.
function.prototype.call, function.prototype.bind, function.prototype.apply과 같은 메서드를 사용할 수 있다.
const Person = function (name) {
this.name = name;
};
const foo = {};
Person.apply(foo, ['name']);
// apply 메소드는 생성자함수 Person을 호출한다. 이때 this에 객체 foo를 바인딩한다.
console.log(foo); // { name: 'name' }
추가 설명
class Counter {
count = 0;
increase = function () {
console.log(this)
}
}
const counter = new Counter();
counter.increase(); // Counter (class)
const caller = counter.increase;
caller(); // undefined
위 코드 중 caller를 실행하면 Counter class가 아닌 이유는, this가 Counter class를 가리키고 있었는데,
Counter increase 포인터를 caller라는 변수로 할당했기 때문에, this의 정보를 잃어버리게 됐다.
let과 const로 할당한 변수는 window에 등록되어 있지 않음으로, caller를 호출하는 것은 어느 것도 아니기 때문에, undefined로 나타난다.
this라는 정보를 다른 곳으로 할당하는 순간 잃어버릴 수 있기 때문에,
this를 묶어주려면 bind를 이용해야 한다.
class Counter {
count = 0;
increase = function () {
console.log(this)
}
}
const counter = new Counter();
counter.increase(); // Counter (class)
const caller = counter.increase;
caller(); // undefined
const bindCaller = counter.increase.bind(counter);
bindCaller(); // Counter (class)
arrow function 이용하면, binding을 일일히 지정 안해줘도 된다
class Counter {
count = 0;
increase = () => {
console.log(this)
}
}
const counter = new Counter();
counter.increase(); // Counter (class)
const caller = counter.increase;
caller(); // Counter (class)
+++ 추가 공부
function temp() {
console.log(this);
}
temp(); // Window 객체
할수를 정의하면 global 객체에서 접근이 가능합니다. 함수를 선언하고, window에서 접근이 가능한 것을 확인할 수 있습니다.
출처
www.zerocho.com/category/NodeJS/post/5b67e8607bbbd3001b43fd7b