“this”키워드는 함수 내에서 어떻게 작동합니까?


248

JavaScript에서 흥미로운 상황이 발생했습니다. 객체 리터럴 표기법을 사용하여 여러 객체를 정의하는 메소드가있는 클래스가 있습니다. 해당 개체 내에서 this포인터가 사용 중입니다. 프로그램의 동작에서, this포인터가 리터럴에 의해 생성되는 객체가 아니라 메소드가 호출 된 클래스를 참조 한다고 추론했습니다 .

이것이 내가 예상하는 방식이지만 임의적 인 것처럼 보입니다. 이것이 정의 된 동작입니까? 브라우저 간 안전한가요? 그것이 "사양이 말하는 것"이외의 이유가 무엇인지에 대한 근거가 있습니까? 파싱 ​​된 코드 예제 :

// inside class definition, itself an object literal, we have this function:
onRender: function() {

    this.menuItems = this.menuItems.concat([
        {
            text: 'Group by Module',
            rptletdiv: this
        },
        {
            text: 'Group by Status',
            rptletdiv: this
        }]);
    // etc
}

내가 그렇게 할 때도 일어난다 var signup = { onLoadHandler:function(){ console.log(this); return Type.createDelegate(this,this._onLoad); }, _onLoad: function (s, a) { console.log("this",this); }};
Deeptechtons


이 게시물을 확인하십시오 . 이 키워드의 다양한 사용법과 행동에 대한 좋은 설명이 있습니다.
Love Hasija

답변:


558

내 다른 게시물에서 식인종 처리되었습니다. 여기에 대해 알고 싶은 것 이상 있습니다.

시작하기 전에 Javascript에 대해 명심하고 이해가되지 않을 때 자신에게 반복하는 것이 가장 중요합니다. Javascript에는 클래스가 없습니다 (ES6 class구문 설탕입니다 ). 어떤 것이 수업처럼 보인다면 영리한 속임수입니다. Javascript에는 객체함수가 있습니다. (그것은 100 % 정확하지는 않지만 함수는 객체 일뿐이지만 때로는 별도의 것으로 생각하면 도움이 될 수 있습니다)

변수 함수에 부착된다. 당신이 함수를 호출 할 때마다, 방법 함수 호출에 따라 특정 값이 부여됩니다. 이것을 종종 호출 패턴이라고합니다.

자바 스크립트에서 함수를 호출하는 방법에는 네 가지가 있습니다. 당신은 같은 함수를 호출 할 수있는 방법 A와, 기능 A와, 생성자 와 함께하는 것은 적용 .

방법으로

메소드는 객체에 첨부 된 함수입니다

var foo = {};
foo.someMethod = function(){
    alert(this);
}

방법으로 호출되면, 이는 기능 / 방법의 일부 객체에 바인딩한다. 이 예에서는 foo에 바인딩됩니다.

기능으로

독립형 함수가있는 경우, 변수는 "글로벌"객체, 브라우저의 컨텍스트에서 거의 항상 객체에 바인딩됩니다 .

 var foo = function(){
    alert(this);
 }
 foo();

이것은 당신을 방해하는 것일 수도 있지만 나쁘지 않습니다. 많은 사람들이 이것을 나쁜 설계 결정으로 생각합니다. 콜백은 메소드가 아닌 함수로 호출되기 때문에 일관성이없는 것으로 보이는 이유가 표시됩니다.

많은 사람들이 이런 식으로 문제를 해결합니다.

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

this 를 가리키는 변수 정의합니다 . 클로저 (자체 주제)는 주위를 유지 하므로 콜백으로 bar를 호출하면 여전히 참조가 있습니다.

참고 : use strict기능으로 사용되는 모드 에서는 this전역에 바인딩되지 않습니다. (입니다 undefined).

생성자로서

함수를 생성자로 호출 할 수도 있습니다. (TestObject)를 사용하는 이름 지정 규칙을 기반으로 이것은 또한 수행중인 작업 일 수도 있고 걸려 넘어지는 작업 일 수도 있습니다 .

새 키워드를 사용하여 생성자로 함수를 호출합니다.

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

생성자로 호출하면 새로운 객체가 생성되고, 이것은 그 개체에 바인딩됩니다. 당신이 내부 기능을 가지고 그들이 콜백으로 사용하는 경우 다시 말하지만, 당신은 함수로 호출 할 것이고, 이것은 전역 객체에 바인딩됩니다. 그 var that = this trick / pattern을 사용하십시오.

어떤 사람들은 생성자 / 새 키워드가 클래스와 비슷한 것을 만드는 방법으로 Java / 전통 OOP 프로그래머에게 던져진 뼈라고 생각합니다.

Apply 방법으로

마지막으로 모든 함수에는 "apply"라는 메소드가 있습니다 (예, 함수는 Javascript의 객체 임). Apply를 사용하면 무엇인지 결정할 수 있으며 인수 배열을 전달할 수도 있습니다. 쓸모없는 예가 있습니다.

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);

8
참고 :에서 엄격 모드 , this될 것입니다 undefined함수 호출을 위해.
Miscreant

1
함수 선언 function myfunction () {}은 "this"가 전역 범위 (창) 인 "방법으로서"의 특수한 경우입니다.
richard

1
@richard : 엄격 모드를 제외하고 this범위 와 관련 이 없습니다. 당신은 전역 객체를 의미 합니다 .
TJ Crowder

@ alan-storm. 의 경우 "생성자으로"입니다 this.confusing = 'hell yeah';같은 var confusing = 'hell yeah';? 그래서 둘 다 허용 myObject.confusing합니까? this내부 작업에 대한 속성 및 기타 변수를 만드는 데 사용할 수 있도록하면 좋지 않을 것입니다.
wunth

하지만 다시 나는 작업 물건이 함수와 생성자를 통해 전달 된 값 외부에서 수행 될 수 있다는 추측 : function Foo(thought){ this.confusing = thought; }다음var myObject = new Foo("hell yeah");
wunth

35

함수 호출

함수는 단지 일종의 객체입니다.

모든 Function 객체에는 호출 된 Function 객체 를 실행하는 호출적용 메소드가 있습니다.

이 메소드에 대한 첫 번째 인수 this는 함수를 실행하는 동안 키워드 가 참조 할 오브젝트를 지정합니다. null또는 undefined오브젝트 인 경우 전역 오브젝트 window가에 사용됩니다 this.

따라서 함수를 호출하면 ...

whereAmI = "window";

function foo()
{
    return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}

... 괄호 - foo()- 동등 foo.call(undefined)또는 foo.apply(undefined)효과적으로 동일 foo.call(window)하거나 foo.apply(window).

>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"

추가 인수 call는 함수 호출에 인수로 전달되는 반면 단일 추가 인수 apply는 함수 호출에 대한 인수를 Array와 같은 객체로 지정할 수 있습니다.

따라서, foo(1, 2, 3)동등 foo.call(null, 1, 2, 3)하거나 foo.apply(null, [1, 2, 3]).

>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"

함수가 객체의 속성 인 경우 ...

var obj =
{
    whereAmI: "obj",
    foo: foo
};

... 목적에 따라 기능에 대한 참조에 액세스 괄호로 호출 - obj.foo()- 동등 foo.call(obj)하거나 foo.apply(obj).

그러나 객체의 속성으로 유지되는 함수는 해당 객체에 "바인드"되지 않습니다. obj위 의 정의에서 볼 수 있듯이 함수는 객체 유형일 뿐이므로 참조 할 수 있습니다 (따라서 함수 호출에 대한 참조로 전달되거나 함수 호출에서 참조로 반환 될 수 있음). 함수에 대한 참조가 전달 될 때, 그것이 전달에 대한 추가 정보 에서 입니다 그것으로 수행되고, 다음과 같은 변화가 없습니다 이유 :

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

함수 참조 baz에 대한 호출은 호출에 대한 컨텍스트를 제공하지 않으므로 효과적으로와 동일 baz.call(undefined)하므로 this참조 window합니다. 우리가 원하는 경우 baz가에 속한다는 것을 알고 obj, 우리가 어떻게 든 그 정보를 제공 할 필요가 baz있다라고를 위치로 첫 번째 인수 call또는 apply폐쇄가 활동하기 시작합니다.

스코프 체인

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

함수가 실행되면 새 범위가 만들어지고 모든 범위에 대한 참조가 있습니다. 위의 예제에서 익명 함수를 만들면 해당 함수가 만들어진 범위 (의 범위)에 대한 참조가 bind있습니다. 이것을 "클로저"라고합니다.

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

변수에 액세스하려고 할 때이 "범위 체인"은 주어진 이름을 가진 변수를 찾기 위해 안내됩니다. 현재 범위에 변수가 포함되어 있지 않으면 체인의 다음 범위를 확인합니다. 글로벌 범위. 익명 함수가 반환되고 bind실행이 완료 되면 익명 함수는 여전히 bind범위에 대한 참조를 가지 므로 bind범위가 "이탈"되지 않습니다.

위의 모든 사항을 감안할 때 이제 다음 예제에서 범위가 작동하는 방식과 특정 값으로 "사전 바인딩 된"함수를 전달하는 기술이 작동이라고하는 이유를 이해할 수 this있습니다.

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"

"함수에 대한 참조가 전달되면 전달 된 위치에 대한 추가 정보가 전달되지 않습니다." @insin에게 감사합니다.
Alex Marandon

9

이것이 정의 된 동작입니까? 브라우저 간 안전한가요?

예. 그리고 그렇습니다.

그것이 왜 그런지에 대한 근거가 있습니까?

그 의미 this 추론하기 매우 간단합니다.

  1. 경우 this생성자 함수 내에서 사용되며, 함수가 호출 된 new키워드this 생성 될 객체를 참조합니다. this공개 메소드에서도 객체를 계속 의미합니다.
  2. this중첩 된 보호 기능을 포함하여 다른 곳에서 사용되는 경우 전역 범위 (브라우저의 경우에는 창 개체)를 가리 킵니다.

두 번째 경우는 분명히 디자인 결함이지만 클로저를 사용하여 해결하기가 쉽습니다.


4

이 경우 내부 thisthis외부 함수 의 변수 대신 전역 객체에 바인딩됩니다 . 언어가 설계된 방식입니다.

자세한 설명은 Douglas Crockford의 "자바 스크립트 : 좋은 부분"을 참조하십시오.


4

ECMAScript 에 대한 훌륭한 자습서를 찾았습니다.

이 값은 실행 컨텍스트와 관련된 특수 객체입니다. 따라서 컨텍스트 객체 (즉, 컨텍스트가 실행 컨텍스트가 활성화 된 객체)로 명명 될 수 있습니다.

컨텍스트의이 값으로 모든 개체를 사용할 수 있습니다.

이 값은 실행 컨텍스트의 속성이지만 변수 객체의 속성은 아닙니다.

변수와 달리이 값은 식별자 확인 프로세스에 참여하지 않기 때문에이 기능은 매우 중요합니다. 즉, 코드 에서이 코드에 액세스하면 해당 값은 실행 컨텍스트에서 직접 가져오고 범위 체인 조회가 없습니다. 이 값은 컨텍스트를 입력 할 때 한 번만 결정됩니다.

글로벌 컨텍스트에서이 값은 글로벌 오브젝트 자체입니다 (즉,이 값은 가변 오브젝트와 같습니다).

함수 컨텍스트의 경우 모든 단일 함수 호출에서이 값이 다를 수 있습니다

참조 자바 스크립트 - 더 - 코어장-3이


" 글로벌 컨텍스트에서이 값은 글로벌 오브젝트 자체입니다 (즉,이 값은 변수 오브젝트와 같습니다) . 전역 객체는 제 (ES4) "변수 객체"및 ES5 환경 레코드이기 때문에 글로벌 실행 컨텍스트의 일부이다. 그러나 그것들은 전역 객체와 다른 엔티티입니다 (예를 들어 환경 레코드는 직접 참조 할 수 없으며 사양에 의해 금지되어 있지만 전역 객체는 가능합니다).
RobG
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.