JavaScript에서는 this
의 값을 계산하기가 까다로울 수 있습니다. 방법은 다음과 같습니다.
JavaScript의 this
는 많은 농담의 핵심입니다. 꽤 복잡하기 때문입니다.
하지만 개발자가 이 this
를 처리하지 않기 위해 훨씬 더 복잡한 도메인 관련 작업을 실행하는 것을 확인했습니다. this
에 대해 잘 모르는 경우 도움이 될 수 있습니다. 저는 this
가이드입니다.
가장 구체적인 상황부터 시작하여 가장 구체적이지 않은 상황으로 마무리하겠습니다. 이 문서는 큰 if (…) … else if () … else if (…) …
와 비슷하므로 지금 보고 있는 코드와 일치하는 첫 번째 섹션으로 바로 이동할 수 있습니다.
- 함수가 화살표 함수로 정의된 경우
- 아니면 함수/클래스가
new
로 호출되는 경우 - 그렇지 않은 경우 함수에 '바인드된'
this
값이 있는 경우 - 통화 시간에
this
가 설정된 경우 - 그렇지 않은 경우 함수가 상위 객체 (
parent.func()
)를 통해 호출되는 경우 - 그렇지 않은 경우 함수 또는 상위 범위가 엄격 모드인 경우
- 그렇지 않은 경우
함수가 화살표 함수로 정의된 경우:
const arrowFunction = () => {
console.log(this);
};
이 경우 this
의 값은 상위 범위의 this
와 항상 동일합니다.
const outerThis = this;
const arrowFunction = () => {
// Always logs `true`:
console.log(this === outerThis);
};
화살표 함수는 this
의 내부 값을 변경할 수 없고 외부 this
와 항상 동일하기 때문에 유용합니다.
기타 예
화살표 함수를 사용하면 this
값을 bind
로 변경할 수 없습니다.
// Logs `true` - bound `this` value is ignored:
arrowFunction.bind({foo: 'bar'})();
화살표 함수를 사용하면 this
의 값을 call
또는 apply
로 변경할 수 없습니다.
// Logs `true` - called `this` value is ignored:
arrowFunction.call({foo: 'bar'});
// Logs `true` - applied `this` value is ignored:
arrowFunction.apply({foo: 'bar'});
화살표 함수를 사용하면 함수를 다른 객체의 멤버로 호출하여 this
값을 변경할 수 없습니다.
const obj = {arrowFunction};
// Logs `true` - parent object is ignored:
obj.arrowFunction();
화살표 함수를 사용하면 함수를 생성자로 호출하여 this
값을 변경할 수 없습니다.
// TypeError: arrowFunction is not a constructor
new arrowFunction();
'바인드된' 인스턴스 메서드
인스턴스 메서드에서 this
가 항상 클래스 인스턴스를 참조하도록 하려면 화살표 함수와 클래스 필드를 사용하는 것이 가장 좋습니다.
class Whatever {
someMethod = () => {
// Always the instance of Whatever:
console.log(this);
};
}
이 패턴은 인스턴스 메서드를 구성요소 (예: React 구성요소 또는 웹 구성요소)의 이벤트 리스너로 사용할 때 매우 유용합니다.
위의 코드는 'this
가 상위 범위의 this
와 동일합니다' 규칙을 깨는 것처럼 보일 수 있지만 생성자에서 설정을 위한 구문 설탕으로 클래스 필드를 생각하면 이해하기 쉽습니다.
class Whatever {
someMethod = (() => {
const outerThis = this;
return () => {
// Always logs `true`:
console.log(this === outerThis);
};
})();
}
// …is roughly equivalent to:
class Whatever {
constructor() {
const outerThis = this;
this.someMethod = () => {
// Always logs `true`:
console.log(this === outerThis);
};
}
}
대체 패턴에는 생성자에서 기존 함수를 결합하거나 생성자에서 함수를 할당하는 작업이 포함됩니다. 어떤 이유로 클래스 필드를 사용할 수 없는 경우 생성자에 함수를 할당하는 것이 적합한 대안입니다.
class Whatever {
constructor() {
this.someMethod = () => {
// …
};
}
}
그 외에 함수/클래스가 new
로 호출되는 경우:
new Whatever();
위의 함수는 this
를 Object.create(Whatever.prototype)
의 결과로 설정하여 Whatever
(또는 클래스인 경우 생성자 함수)를 호출합니다.
class MyClass {
constructor() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
}
// Logs `true`:
new MyClass();
이전 스타일 생성자의 경우도 마찬가지입니다.
function MyClass() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
// Logs `true`:
new MyClass();
기타 예
new
로 호출되는 경우 this
의 값은 bind
를 사용하여 변경할 수 없습니다.
const BoundMyClass = MyClass.bind({foo: 'bar'});
// Logs `true` - bound `this` value is ignored:
new BoundMyClass();
new
로 호출하면 함수를 다른 객체의 멤버로 호출해도 this
값을 변경할 수 없습니다.
const obj = {MyClass};
// Logs `true` - parent object is ignored:
new obj.MyClass();
그렇지 않고 함수에 '바인드된' this
값이 있는 경우:
function someFunction() {
return this;
}
const boundObject = {hello: 'world'};
const boundFunction = someFunction.bind(boundObject);
boundFunction
가 호출될 때마다 this
값은 bind
(boundObject
)에 전달되는 객체가 됩니다.
// Logs `false`:
console.log(someFunction() === boundObject);
// Logs `true`:
console.log(boundFunction() === boundObject);
기타 예
바인딩된 함수를 호출할 때 this
의 값은 call
또는 apply
를 사용하여 변경할 수 없습니다.
// Logs `true` - called `this` value is ignored:
console.log(boundFunction.call({foo: 'bar'}) === boundObject);
// Logs `true` - applied `this` value is ignored:
console.log(boundFunction.apply({foo: 'bar'}) === boundObject);
바인딩된 함수를 호출할 때는 함수를 다른 객체의 멤버로 호출해도 this
값을 변경할 수 없습니다.
const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);
그 외에 통화 시간에 this
가 설정된 경우에는 다음을 충족해야 합니다.
function someFunction() {
return this;
}
const someObject = {hello: 'world'};
// Logs `true`:
console.log(someFunction.call(someObject) === someObject);
// Logs `true`:
console.log(someFunction.apply(someObject) === someObject);
this
값은 call
/apply
에 전달되는 객체입니다.
안타깝게도 this
는 DOM 이벤트 리스너와 같은 요소에 의해 다른 값으로 설정되며, 이를 사용하면 이해하기 어려운 코드가 생성될 수 있습니다.
element.addEventListener('click', function (event) { // Logs `element`, since the DOM spec sets `this` to // the element the handler is attached to. console.log(this); });
위와 같은 경우에는 this
를 사용하지 않고 대신 다음과 같이 합니다.
element.addEventListener('click', (event) => { // Ideally, grab it from a parent scope: console.log(element); // But if you can't do that, get it from the event object: console.log(event.currentTarget); });
그렇지 않고 함수가 상위 객체 (parent.func()
)를 통해 호출되는 경우 다음과 같습니다.
const obj = {
someMethod() {
return this;
},
};
// Logs `true`:
console.log(obj.someMethod() === obj);
이 경우 함수는 obj
의 멤버로 호출되므로 this
는 obj
입니다. 이 문제는 호출 시
발생하므로 함수가 상위 객체 없이 호출되거나 다른 상위 객체를 사용하여 호출되면 링크가
끊어집니다.
const {someMethod} = obj;
// Logs `false`:
console.log(someMethod() === obj);
const anotherObj = {someMethod};
// Logs `false`:
console.log(anotherObj.someMethod() === obj);
// Logs `true`:
console.log(anotherObj.someMethod() === anotherObj);
someMethod() === obj
는 false입니다. someMethod
가 obj
의 멤버로 호출되지 않기 때문입니다. 다음과 같이 시도할 때 이 문제가 발생할 수 있습니다.
const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
이 경우 querySelector
구현이 자체 this
값을 보고 이 값이 일종의 DOM 노드가 될 것으로 예상하고 위에서 연결이 끊어지기 때문에 연결이 끊어집니다. 위 목표를 올바르게 달성하려면 다음 안내를 따르세요.
const $ = document.querySelector.bind(document);
// Or:
const $ = (...args) => document.querySelector(...args);
재미있는 사실: 일부 API는 내부적으로 this
를 사용하지 않습니다. console.log
와 같은 콘솔 메서드가 this
참조를 피하기 위해 변경되었으므로 log
를 console
에 바인딩할 필요가 없습니다.
그렇지 않고 함수 또는 상위 범위가 엄격 모드인 경우 다음을 충족해야 합니다.
function someFunction() {
'use strict';
return this;
}
// Logs `true`:
console.log(someFunction() === undefined);
이 경우 this
의 값은 정의되지 않습니다. 상위 범위가 엄격 모드이고 모든 모듈이 엄격 모드인 경우 함수에 'use strict'
가 필요하지 않습니다.
그 이외의 경우
function someFunction() {
return this;
}
// Logs `true`:
console.log(someFunction() === globalThis);
이 경우 this
값은 globalThis
와 동일합니다.
휴!
이상입니다 this
에 대해 제가 알고 있는 정보는 여기까지입니다. 질문이 있으신가요? 놓친 부분이 있나요? 언제든지 트윗해 주세요.