콜백 내에서 올바른`this`에 액세스하는 방법은 무엇입니까?
이벤트 처리기를 등록하는 생성자 함수가 있습니다.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
그러나 data
콜백 내에서 생성 된 객체 의 속성에 액세스 할 수 없습니다 . this
생성 된 개체가 아니라 다른 개체를 참조하는 것 같습니다 .
또한 익명 함수 대신 개체 메서드를 사용하려고했습니다.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
그러나 그것은 동일한 문제를 나타냅니다.
올바른 개체에 액세스하려면 어떻게해야합니까?
답변
알아야 할 사항 this
this
(일명 "컨텍스트")는 각 함수 내부의 특수 키워드이며 그 값 은 함수가 어떻게 / 언제 / 어디에서 정의 되었는지가 아니라 함수가 어떻게 호출 되었는지에 따라 달라집니다 . 다른 변수와 같은 어휘 범위의 영향을받지 않습니다 (화살표 함수 제외, 아래 참조). 여기 예시들이 있습니다 :
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
더에 대한 자세한 내용은 this
, 상기보고가 MDN 문서를 .
올바른 것을 참조하는 방법 this
화살표 기능 사용
ECMAScript 6 은 람다 함수로 생각할 수있는 화살표 함수를 도입했습니다 . 그들은 자신의 this
바인딩 이 없습니다 . 대신 this
일반 변수처럼 범위 내에서 조회됩니다. 즉,에게 전화 할 필요가 없습니다 .bind
. 이것이 그들이 가진 유일한 특별한 행동은 아닙니다. 자세한 내용은 MDN 문서를 참조하십시오.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
사용하지 마십시오 this
실제로 this
특별히 액세스하고 싶지는 않지만 참조하는 객체 입니다. 그렇기 때문에 쉬운 해결책은 단순히 해당 객체를 참조하는 새 변수를 만드는 것입니다. 변수는 어떤 이름도 가질 수 있지만 일반적인 이름은 self
및 that
입니다.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
self
은 일반 변수 이므로 어휘 범위 규칙을 따르고 콜백 내에서 액세스 할 수 있습니다. 또한 this
콜백 자체 의 값에 액세스 할 수 있다는 장점도 있습니다.
명시 적으로 this
콜백 설정 -1 부
this
값이 자동으로 설정되기 때문에 의 값을 제어 할 수없는 것처럼 보일 수 있지만 실제로는 그렇지 않습니다.
모든 함수에는 값에 바인딩 된 새 함수를 반환하는 .bind [docs] 메서드 this
가 있습니다. 이 함수는 사용자가 호출 한 것과 정확히 동일한 동작을 수행하며 사용자 가 설정 한 동작 .bind
만 this
있습니다. 해당 함수가 호출되는 방법이나시기에 관계없이 this
항상 전달 된 값을 참조합니다.
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
이 경우, 우리는 콜백의 결합되는 this
의 값 MyConstructor
의 this
.
참고 : jQuery에 대한 바인딩 컨텍스트 인 경우 대신 jQuery.proxy [docs]를 사용하십시오. 이렇게하는 이유는 이벤트 콜백을 바인딩 해제 할 때 함수에 대한 참조를 저장할 필요가 없기 때문입니다. jQuery는이를 내부적으로 처리합니다.
this
콜백 세트 -2 부
콜백을 허용하는 일부 함수 / 메소드는 콜백 this
이 참조해야하는 값도 허용합니다 . 이것은 기본적으로 직접 바인딩하는 것과 동일하지만 함수 / 메서드가 자동으로 수행합니다. Array#map [문서] 는 그러한 방법입니다. 서명은 다음과 같습니다.
array.map(callback[, thisArg])
첫 번째 인수는 콜백이고 두 번째 인수는 this
참조해야하는 값 입니다. 다음은 인위적인 예입니다.
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
참고 : 값을 전달할 수 있는지 여부 this
는 일반적으로 해당 함수 / 메서드의 문서에 언급되어 있습니다. 예를 들어 jQuery의 $.ajax메소드 [docs] 는 다음과 같은 옵션을 설명합니다 context
.
이 객체는 모든 Ajax 관련 콜백의 컨텍스트가됩니다.
일반적인 문제 : 객체 메서드를 콜백 / 이벤트 핸들러로 사용
이 문제의 또 다른 일반적인 현상은 객체 메서드가 콜백 / 이벤트 핸들러로 사용될 때입니다. 함수는 JavaScript에서 일류 시민이며 "메서드"라는 용어는 객체 속성의 값인 함수에 대한 구어체 용어 일뿐입니다. 그러나 그 함수에는 "포함"개체에 대한 특정 링크가 없습니다.
다음 예를 고려하십시오.
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
이 함수 this.method
는 클릭 이벤트 처리기로 할당 document.body
되지만를 클릭하면 undefined
이벤트 처리기 내부에서 의 인스턴스가 아닌을 this
참조 하므로 기록되는 값은 document.body
입니다 Foo
.
처음에 이미 언급했듯이, this
참조하는 것은 함수 가 정의 된 방식이 아니라 함수가 호출 되는 방식에 따라 다릅니다 . 코드가 다음과 같으면 함수에 객체에 대한 암시 적 참조가 없다는 것이 더 분명 할 수 있습니다.
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
솔루션 은 위에서 언급 한 것과 동일합니다. 가능한 경우 특정 값에 .bind
명시 적으로 바인딩 this
하는 데 사용합니다.
document.body.onclick = this.method.bind(this);
또는 익명 함수를 콜백 / 이벤트 핸들러로 사용하여 함수를 객체의 "메서드"로 명시 적으로 호출하고 객체 ( this
)를 다른 변수에 할당합니다 .
var self = this;
document.body.onclick = function() {
self.method();
};
또는 화살표 기능을 사용하십시오.
document.body.onclick = () => this.method();
다음은 자식 컨텍스트 내에서 부모 컨텍스트에 액세스하는 몇 가지 방법입니다.
bind()
기능 을 사용할 수 있습니다 .- 다른 변수 안에 context / this에 대한 참조를 저장합니다 (아래 예제 참조).
- ES6 Arrow 기능을 사용하십시오 .
- 코드 / 기능 디자인 / 아키텍처 변경-이를 위해서는 자바 스크립트의 디자인 패턴 에 대한 명령이 있어야합니다 .
1. bind()
기능 사용
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', ( function () {
alert(this.data);
}).bind(this) );
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
사용하는 경우 underscore.js
-http://underscorejs.org/#bind
transport.on('data', _.bind(function () {
alert(this.data);
}, this));
2 컨텍스트 / this에 대한 참조를 다른 변수 안에 저장합니다.
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
3 화살표 기능
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
메서드 호출의 "마법"구문에 모두 있습니다.
object.property();
객체에서 속성을 가져 와서 한 번에 호출하면 객체가 메서드의 컨텍스트가됩니다. 동일한 메서드를 호출하지만 별도의 단계에서 컨텍스트는 대신 전역 범위 (창)입니다.
var f = object.property;
f();
메서드의 참조를 가져 오면 더 이상 객체에 연결되지 않고 일반 함수에 대한 참조 일뿐입니다. 콜백으로 사용할 참조를 가져올 때도 마찬가지입니다.
this.saveNextLevelData(this.setAll);
여기서 컨텍스트를 함수에 바인딩합니다.
this.saveNextLevelData(this.setAll.bind(this));
jQuery를 사용하는 경우 모든 브라우저에서 지원되지 않으므로 $.proxy
대신 메서드를 사용해야 bind
합니다.
this.saveNextLevelData($.proxy(this.setAll, this));
"컨텍스트"의 문제점
"컨텍스트"라는 용어는이에서 참조하는 객체를 가리키는 데 사용되기도 합니다 . 의미 적으로나 기술적으로 ECMAScript의 this 와 맞지 않기 때문에 그 사용은 부적절 합니다 .
"컨텍스트" 는 의미를 추가하는 무언가를 둘러싼 상황 또는 추가 의미를 제공하는 일부 앞뒤 정보를 의미합니다. 용어 "콘텍스트"를 참조로 사용되는 ECMAScript를 실행 콘텍스트 의 모든 파라미터의 범위이고, 및 이 일부의 실행 코드의 범위 내.
이는 ECMA-262 섹션 10.4.2에 나와 있습니다 .
ThisBinding을 호출 실행 컨텍스트의 ThisBinding과 동일한 값으로 설정합니다.
이것은 이것이 실행 컨텍스트의 일부 임을 명확하게 나타냅니다 .
실행 컨텍스트는 실행중인 코드에 의미를 추가하는 주변 정보를 제공합니다. thisBinding 보다 훨씬 더 많은 정보를 포함합니다 .
의 값 그래서 이 "컨텍스트"아니다, 그것은 실행 컨텍스트의 한 부분입니다. 본질적으로 모든 객체에 대한 호출과 엄격 모드에서 모든 값으로 설정할 수있는 지역 변수입니다.
"this"키워드에 대해 알아야합니다.
내 견해에 따라 세 가지 방법으로 "this"를 구현할 수 있습니다 (Self / Arrow 기능 / Bind 방법).
함수의 this 키워드는 다른 언어에 비해 JavaScript에서 약간 다르게 작동합니다.
또한 엄격 모드와 비 엄격 모드간에 약간의 차이가 있습니다.
대부분의 경우이 값은 함수가 호출되는 방식에 따라 결정됩니다.
실행 중 할당으로 설정할 수 없으며 함수가 호출 될 때마다 다를 수 있습니다.
ES5는 호출 방법에 관계없이 함수의 값을 설정하기 위해 bind () 메서드를 도입했습니다.
ES2015는 자체적으로이 바인딩을 제공하지 않는 화살표 함수를 도입했습니다 (둘러싸는 어휘 컨텍스트의이 값을 유지함).
방법 1 : Self-Self는 문맥이 변하더라도 원본에 대한 참조를 유지하는 데 사용됩니다. 이벤트 핸들러 (특히 클로저)에서 자주 사용되는 기술입니다.
참조 :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function () {
alert(self.data);
});
}
방법 2 : 화살표 함수-화살표 함수 표현식은 정규 함수 표현식에 대한 구문 적으로 간결한 대안입니다.
this, arguments, super 또는 new.target 키워드에 대한 자체 바인딩은 없지만.
화살표 함수 식은 메서드로 적합하지 않으며 생성자로 사용할 수 없습니다.
참조 :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
Method3 : Bind- bind () 메서드는 다음과 같은 새 함수를 만듭니다.
호출되면 this 키워드가 제공된 값으로 설정됩니다.
새 함수가 호출 될 때 제공된 인수 앞에 주어진 인수 시퀀스가 있습니다.
참고: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
첫째, 의 컨텍스트에서 키워드에 대한 명확한 이해 scope
와 동작 이 필요합니다 .this
scope
this
& scope
:
there are two types of scope in javascript. They are :
1) Global Scope
2) Function Scope
즉, 글로벌 스코프는 윈도우 객체를 의미하며, 글로벌 스코프에 선언 된 변수는 어디에서나 접근 할 수 있습니다. 반면에 함수 스코프는 함수 내부에 상주하며, 함수 내부에 선언 된 변수는 외부에서 정상적으로 접근 할 수 없습니다. this
전역 범위의 키워드는 창 개체를 참조합니다. this
inside function은 또한 window 객체 this
를 참조하므로, 우리가 this
선택한 컨텍스트를 나타 내기 위해 조작하는 방법을 찾을 때까지 항상 window를 참조합니다 .
--------------------------------------------------------------------------------
- -
- Global Scope -
- ( globally "this" refers to window object) -
- -
- function outer_function(callback){ -
- -
- // outer function scope -
- // inside outer function"this" keyword refers to window object - -
- callback() // "this" inside callback also refers window object -
- } -
- -
- function callback_function(){ -
- -
- // function to be passed as callback -
- -
- // here "THIS" refers to window object also -
- -
- } -
- -
- outer_function(callback_function) -
- // invoke with callback -
--------------------------------------------------------------------------------
this
콜백 함수 내부 를 조작하는 다양한 방법 :
여기에 Person이라는 생성자 함수가 있습니다. 그것은라는 특성이 name
및라는 네 가지 방법을 sayNameVersion1
, sayNameVersion2
, sayNameVersion3
, sayNameVersion4
. 네 가지 모두 하나의 특정 작업이 있습니다. 콜백을 수락하고 호출합니다. 콜백에는 Person 생성자 함수 인스턴스의 이름 속성을 기록하는 특정 작업이 있습니다 .The callback has a specific task that is to log the name property of an instance of Person constructor function.
function Person(name){
this.name = name
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
this.sayNameVersion3 = function(callback){
callback.call(this)
}
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
}
function niceCallback(){
// function to be used as callback
var parentObject = this
console.log(parentObject)
}
이제 사람 생성자에서 인스턴스를 만들고 sayNameVersionX
(X는 1,2,3,4) 메서드 의 다른 버전을 호출 하여 인스턴스 를 참조하기 위해 내부 콜백을 niceCallback
조작 할 수있는 방법을 확인합니다 .this
person
var p1 = new Person('zami') // create an instance of Person constructor
바인딩 :
bind가하는 일은 this
제공된 값으로 설정된 키워드 로 새 함수를 만드는 것 입니다.
sayNameVersion1
그리고 sayNameVersion2
bind를 사용 this
하여 콜백 함수 를 조작 하십시오.
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
첫 번째 this
는 메서드 자체 내부에서 콜백으로 바인딩 하고 두 번째 콜백은 객체가 바인딩 된 상태로 전달됩니다.
p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method
p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
전화 :
first argument
의 call
방법으로 사용 this
하여 호출 된 함수 내 call
첨부.
sayNameVersion3
창 개체 대신 우리가 만든 사람 개체를 참조하기 위해 call
를 조작하는 데 사용 됩니다 this
.
this.sayNameVersion3 = function(callback){
callback.call(this)
}
다음과 같이 호출됩니다.
p1.sayNameVersion3(niceCallback)
적용 :
와 유사하게 call
의 첫 번째 인수는 키워드 apply
로 표시 될 객체를 나타냅니다 this
.
sayNameVersion4
사람 객체를 참조하기 apply
위해 조작 this
하는 데 사용
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
다음과 같이 호출되며, 콜백이 전달 되기만하면됩니다.
p1.sayNameVersion4(niceCallback)
콜백 함수에서 컨텍스트에 액세스 하려면 다음 과 같이 달성 할 수있는 콜백 함수 를 사용하여 setTimeout()
항상 전역 객체 (Window)로 실행되므로이를에 바인딩 할 수 없습니다 .this
bind()
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
문제는 this
키워드가 자바 스크립트에서 어떻게 작동하는지에 관한 것입니다. this
아래와 같이 다르게 동작합니다.
- 의 값
this
은 일반적으로 함수 실행 컨텍스트에 의해 결정됩니다. - 전역 범위
this
에서 전역 개체 (window
개체)를 나타냅니다. - 엄격 모드는 어떤 기능을 사용할 경우 다음의 값이
this
될 것입니다undefined
엄격 모드로는 글로벌 객체를 참조undefined
의 위치에window
객체입니다. - 점 앞에 서있는 개체가이 키워드가 바인딩되는 대상입니다.
- 우리는 명시 적으로이 값을 설정할 수 있습니다
call()
,bind()
그리고apply()
new
키워드 (생성자)가 사용 되면 생성되는 새 객체에 바인딩됩니다.- Arrow 함수는 바인딩되지 않습니다.
this
대신this
어휘 적으로 바인딩됩니다 (즉, 원래 컨텍스트를 기반으로 함).
대부분의 답변에서 알 수 있듯이 Arrow 함수 또는 bind()
Method 또는 Self var를 사용할 수 있습니다 . Google JavaScript Style Guide 에서 람다 (화살표 함수)에 대한 요점을 인용하겠습니다.
f.bind (this), 특히 goog.bind (f, this)보다 화살표 함수를 사용하는 것이 좋습니다. const self = this를 작성하지 마십시오. 화살표 함수는 예기치 않게 추가 인수를 전달하는 콜백에 특히 유용합니다.
Google은 bind 또는 const self = this
따라서 가장 좋은 해결책은 아래와 같이 람다를 사용하는 것입니다.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
참고 문헌 :
- https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
- 화살표 기능 대 바인딩
현재 코드에서 클래스를 사용하는 경우 가능한 다른 접근 방식이 있습니다.
클래스 필드를 지원 하면 다음 방법으로 만들 수 있습니다.
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // this refers to correct value
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
확실히 내부적으로는 컨텍스트를 바인딩하는 모든 오래된 좋은 화살표 함수이지만이 형식에서는 명시 적 바인딩보다 훨씬 더 명확 해 보입니다.
Stage 3 Proposal이므로 지금 당장 (2018 년 8 월) 처리 하려면 babel과 적절한 babel 플러그인 이 필요합니다 .
이벤트 리스너 내에서 바인딩하는 DOM2 이후 표준 방식 인 또 다른 접근 방식은 (다른 이점 중에서) 항상 리스너를 제거this
할 수있게 해주는 인터페이스 의 handleEvent(evt)
메서드입니다 EventListener
.
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
사용에 대한 자세한 정보는 handleEvent
여기에서 찾을 수 있습니다.https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
this
JS에서 :
this
JS에서 의 값은 함수가 정의 된 방식이 아니라 호출되는 방식에 따라 100 % 결정됩니다. 우리는 비교적 쉽게 값을 찾을 수 있습니다 this
에 의해 '도트 규칙의 왼쪽' :
- function 키워드를 사용하여 함수를 만들 때의 값
this
은 호출되는 함수의 점 왼쪽에있는 객체입니다. - 점의 왼쪽에 객체가없는 경우
this
함수 내부 의 값 은 종종 전역 객체 (global
노드,window
브라우저)입니다. 나는this
같은 것을 사용하는 것보다 덜 명시 적이기 때문에 여기 에 키워드를 사용하지 않는 것이 좋습니다window
! Function.prototype.bind()
의 값을 수정할 수있는 함수를 사용하여 만든 화살표 함수 및 함수와 같은 특정 구문 이 있습니다this
. 이것들은 규칙의 예외이지만의 값을 수정하는 데 정말 도움이됩니다this
.
nodeJS의 예
module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);
const obj1 = {
data: "obj1 data",
met1: function () {
console.log(this.data);
},
met2: () => {
console.log(this.data);
},
};
const obj2 = {
data: "obj2 data",
test1: function () {
console.log(this.data);
},
test2: function () {
console.log(this.data);
}.bind(obj1),
test3: obj1.met1,
test4: obj1.met2,
};
obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);
산출:
출력을 1 x 1로 안내해 드리겠습니다 (두 번째부터 시작하는 첫 번째 로그 무시).
this
입니다obj2
때문에 도트 규칙의 왼쪽에, 우리는 어떻게 볼 수test1
라고합니다obj2.test1();
.obj2
점의 왼쪽이므로this
값입니다.obj2
점의 왼쪽 에도 메서드test2
를obj1
통해 바인딩됩니다bind()
. 따라서this
값은obj1
입니다.obj2
라는 함수의 점 왼쪽에obj2.test3()
있습니다. 따라서obj2
의 값이this
됩니다.- 이 경우 :
obj2.test4()
obj2
점의 왼쪽에 있습니다. 그러나 화살표 함수에는 자체this
바인딩 이 없습니다 . 따라서 처음에 기록 된 객체 인this
외부 범위 의 값에 바인딩됩니다module.exports
. - 함수
this
를 사용하여의 값을 지정할 수도call
있습니다. 여기에서 원하는this
값을 인수로 전달할 수 있습니다obj2
.이 경우에 그렇습니다.
다음 과 같이 HTML에서 호출 된 Ngx
꺾은 선형 차트 xAxisTickFormatting
기능에 문제가 발생했습니다 [xAxisTickFormatting]="xFormat"
. 선언 된 함수에서 내 구성 요소의 변수에 액세스 할 수 없습니다. 이 솔루션은 올바른 문제를 찾기 위해 문제를 해결하는 데 도움이되었습니다. 이것이 Ngx
라인 차트, 사용자에게 도움이되기를 바랍니다 .
다음과 같은 함수를 사용하는 대신 :
xFormat (value): string {
return value.toString() + this.oneComponentVariable; //gives wrong result
}
이것을 사용하십시오 :
xFormat = (value) => {
// console.log(this);
// now you have access to your component variables
return value + this.oneComponentVariable
}