[javascript] 콜백 내에서 올바른 ‘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

사용하지 마십시오 this

실제로는 this특히 액세스하고 싶지 않지만 참조 하는 객체 에 액세스 하십시오 . 그렇기 때문에 쉬운 해결책은 단순히 해당 객체를 참조하는 새 변수를 만드는 것입니다. 변수는 임의의 이름을 가질 수 있지만 일반적인 변수는 selfthat입니다.

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의 값 MyConstructorthis.

참고 : jQuery에 대한 컨텍스트를 바인딩 할 때는 jQuery.proxy [docs]를 대신 사용하십시오. 이를 수행하는 이유는 이벤트 콜백을 바인딩 해제 할 때 함수에 대한 참조를 저장할 필요가 없기 때문입니다. jQuery는 내부적으로 처리합니다.

ECMAScript 6 : 화살표 기능 사용

ECMAScript 6에는 화살표 함수가 도입 되어 람다 함수로 생각할 수 있습니다. 그들은 자신의 this바인딩 이 없습니다 . 대신 this일반 변수처럼 범위 내에서 조회됩니다. 즉,에 전화 할 필요가 없습니다 .bind. 이것이 유일한 특수 행동은 아닙니다. 자세한 내용은 MDN 설명서를 참조하십시오.

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

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는 click 이벤트 핸들러로 할당 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();


답변

자식 컨텍스트 내에서 부모 컨텍스트에 액세스하는 몇 가지 방법이 있습니다.

  1. bind()기능 을 사용할 수 있습니다 .
  2. 컨텍스트 / this에 대한 참조를 다른 변수 안에 저장하십시오 (아래 예 참조).
  3. ES6 화살표 기능을 사용하십시오 .
  4. 코드 / 함수 디자인 / 아키텍처 변경-이를 위해서는 자바 스크립트의 디자인 패턴 에 대한 명령이 있어야합니다 .

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);

사용중인 경우 -httpunderscore.js : //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 Method)

함수의이 키워드는 JavaScript에서 다른 언어와 약간 다르게 작동합니다.

또한 엄격 모드와 엄격하지 않은 모드 사이에는 약간의 차이가 있습니다.

대부분의 경우이 값은 함수 호출 방법에 따라 결정됩니다.

실행 중에 할당하여 설정할 수 없으며 함수가 호출 될 때마다 다를 수 있습니다.

ES5는 bind () 메소드를 도입하여 호출 방법에 관계없이 함수의 값을 설정합니다.

그리고 ES2015는 자체적으로이 바인딩을 제공하지 않는 화살표 함수를 도입했습니다 (이것은 묶는 어휘 문맥의이 값을 유지합니다).

방법 1 : 자체-컨텍스트가 변경되는 경우에도 자체를 사용하여 원본에 대한 참조를 유지합니다. 이벤트 처리기 (특히 클로저)에서 자주 사용되는 기술입니다.

참조 : 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);
    });
}

Method2 : 화살표 함수-화살표 함수 표현식은 정규 함수 표현식에 대한 구문 상 컴팩트 한 대안입니다.

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 () 메소드는 새로운 함수를 생성합니다.

호출되면이 키워드가 제공된 값으로 설정되어 있으며

새 함수가 호출 될 때 제공된 인수 앞에 주어진 인수 시퀀스가 ​​있습니다.

참조 : 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와 행동 이 필요합니다 .thisscope

this& scope:


there are two types of scope in javascript. They are :

   1) Global Scope

   2) Function Scope

간단히 말하면 전역 범위는 창 개체를 가리 킵니다. 전역 범위에서 선언 된 변수는 어디에서나 액세스 할 수 있습니다. 반면에 함수 범위는 함수 내부에 있습니다. 함수 내부에서 선언 된 변수는 일반적으로 외부 세계에서 액세스 할 수 없습니다. this전역 범위의 키워드는 창 개체를 나타냅니다. this내부 함수는 또한 윈도우 객체 this를 참조하므로 우리가 this선택한 컨텍스트를 나타 내기 위해 조작 할 수있는 방법을 찾을 때까지 항상 윈도우를 참조합니다 .

--------------------------------------------------------------------------------
-                                                                              -
-   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 생성자 함수 인스턴스의 name 속성을 기록하는 특정 작업이 있습니다.

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)

}

이제 person 생성자에서 인스턴스를 생성하고 sayNameVersionX(X는 1,2,3,4 niceCallback참조 ) 메소드 의 다른 버전을 호출 this하여 person인스턴스 를 참조하기 위해 내부 콜백을 조작하는 방법의 수 를 확인하십시오 .

var p1 = new Person('zami') // create an instance of Person constructor

바인딩 :

바인드는 this키워드를 제공된 값으로 설정하여 새 함수를 작성하는 것 입니다.

sayNameVersion1sayNameVersion2사용 바인드 조작 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 argumentcall방법으로 사용 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(), 그것을 항상 함께 실행 전역 객체 (창) 당신이 액세스하려는 경우, this다음을 사용하여 콜백 함수의 컨텍스트 bind()우리가 달성 할 수있는 콜백 함수에 :

setTimeout(function(){
    this.methodName();
}.bind(this), 2000);