[javascript 이해] this에 대하여 3

in #kr6 years ago

jsimg.png

시작하며

우리의 목적은 this를 이해함으로써, 헷갈리지 않고 this의 범위를 예측 하는 것이었다.

this에 대하여 1
this에 대하여 2

이전 포스트에서 실행환경(Execution Context) 과 언어적 환경(Lexical Environment)을 이야기 했었는데, 이 두가지를 잘 사용하면 된다!

이 부분이 이해가 안된다면 계속 읽고, 연구하고, 연습해봐야 한다. this의 범위를 아는 것은 자바스크립트를 다룰줄 아는 개발자라면 필수적으로 알아야 하는 부분인 것 같다고 생각한다.

함수가 호출될 때 왼쪽에 객체가 있는지만 살펴보면 this가 어디에 바인딩되는지 알 수 있다. (예를 들어 object.function() 인지, function() 단독 호출인지만 보면 된다는 것)

자바스크립트에서 함수가 호출될 수 있는 패턴을 살펴보고, 실전예제를 통해 직접 예측해보자.

목차

메소드 호출 패턴
함수 호출 패턴
생성자 패턴
apply 호출 패턴
콜백 패턴

메소드 호출 패턴

함수가 어떤 객체의 속성에 있는 경우 특별히 메소드라고 부른다.

아래 코드를 준비했다.

그림 1

testObject 는 2개의 멤버가 있는데, 그중 하나가 함수형태로 표현된 method 라는 이름을 가지고 있는 메소드이다. 객체의 속성을 불러오는 방법은 마침표 . 연산자를 이용하거나, 대괄호 [] 안에 해당 속성과 일치하는 이름을 입력해주면 된다.

그럼 우리가 알고싶은 this가 어디에 바인딩 되는지 살펴본다


그림2

2개의 객체를 만들고 그 안에 각각 메소드를 입력했다. testObject 의 메소드는 콘솔에 this를 찍는다. test2Object 도 마찬가지로 this를 찍는다. 그 아래에는 testObject의 메소드를 호출해서 실행시킨다 . test2Object.method2()의 결과는 어떨까?

첫번째에는 test2Object가 찍히고, 두번째에 testObject가 찍힌다.

완벽하게 자신이 속한 객체를 반환시킨다 .

잘 생각하자. thisBinding은 호출된 시점이라고 이야기 했었다.

this를 파악하는 첫번째 방법! 함수를 호출한 (. 연산자나 [속성이름] 따위와 같이 호출한 경우) 객체는 반드시 그 메소드의 this에 바인딩된다.

객체안의 메소드를 불러내려면 필연적으로 객체를 명시할 수 밖에 없는 상황이기때문에, 이러한 형식은 this를 명확히 할 수가 있다는 장점이 있다.

thisBinding이 호출시점에 이뤄짐을 이용해서 더 효율적으로 사용할 수 있다고 한다. 이처럼 자신의 객체문맥을 this로 얻는 메소드를 퍼블릭(public) 메소드라 칭한다.

이런 퍼블릭 메소드로만 구성되면 좋겠지만 코드가 항상 그렇게 짜여지지 않는 경우가 있으니

다양하게 알아보는 것이 좋다.

함수 호출 패턴

함수가 어떤 객체의 속성으로 존재하는 것이 아니라 그 실행환경에서 바로 함수를 지정할때 함수 호출 패턴이라고 부른다.


그림3

이런식으로 지정하고, funcTest()를 실행하면 무엇이 찍힐까.

첫번째 팁을 적용해보자니, 이 함수를 호출한 대상을 모른다. 호출한 대상이 없으면 자바스크립트는 너무나도 당연하게 전역 객체를 this로 삼아버린다. 명심하자, 자바스크립트는 호출한 대상이 없으면 전역객체를 this로 바인딩시켜버린다. 물론 use strict가 적용된 곳이라면 null 값이다. console로 찍어보면 undefined가 나올것이다. 어느곳에도 정의 되지 않았으니...

생성자 패턴

var Ctr = function(){
    console.log(this)
}
function callee(){
    console.log(this)
}
Ctr.prototype.calling= function(){
    console.log(this);
}
var myCtr = new Ctr();
new Ctr();
myCtr.calling();

new 연산자를 이용해서 객체를 생성하는 함수를 생성자라고 부른다.

new 연산자는 객체를 만들어 버리는 함수이기 때문에, 조금 특이하다 .하지만 잘 따라왔다면 이 역시 this의 범위를 파악하는데 큰 문제가 없다.

new 연산자는 내부적으로 프로토타입을 통해 객체를 생성한 후 this를 이 객체에 강제로 바인딩시켜 리턴한다.

코드를 통해 확인해보면,

먼저 Ctr 이라는 this를 찍는 생성자가 있다고 해보자. 생성자가 만들어진 것이지, 객체가 생성된 것은 아니다. 하지만 객체의 프로토타입은 생겼다는 것이다. 앞으로 생성해내는 모든 객체는 calling이라는 메소드가 포함되도록 작성했다.

맨 아래쪽에 3가지 형태가 있다. 각각 살펴보자

첫번째는 myCar라는 변수에 Ctr 객체를 생성해주었다. 생성자는 그 자체가 함수이기때문에, 실행이 된다.

new 연산자를 하나의 객체라고 보면 된다. 앞서 설명했듯이 객체를 만들고 this를 바인딩해버리고 리턴한다.

쉽게 생각하면, Ctr이라는 함수 옆에 new라는 객체가 있다고 생각해보자. new 연산자와 함께 쓰인 함수라면 왼쪽에 자신의 이름을 가지고 있는 객체가 그 함수를 호출했다고 보면된다.

그렇기 때문에 생성자는 일반 함수와 구분해줄 필요가 있다. 이것이 객체인지, 단순 함수인지를 구분하지 않는다면 나중에 식별자가 꼬이는 참사가 발생할테니 말이다.

맨 마지막줄의 경우에는 왼쪽에 객체가 있기때문에, 답은 뻔하다.

Apply 호출 패턴

이 메소드는 권장되는 방법이다. 함수에 this를 직접 지정해서 실행시켜버리는 방식이다.

자세한 방법은 검색을 통해 직접 문서를 읽는것이 더 도움이 될것이라 본다

콜백 패턴

자바스크립트에서는 콜백을 쓰는 경우가 많은데, 콜백함수의 this는 누구일까?

다음 코드를 보자.

var test = function(callback){
    callback();
}
function callee(){
    console.log(this)
}

test라는 함수는 콜백을 받아서 실행시켜버린다. 콜백함수는 this를 출력한다.

정답은 간단하다. this는 그 함수가 어디에 속해있는지 전혀 신경쓰지 않는다. 오로지 호출되는 시점에

왼쪽에 객체가 있는지? 아니면 Apply 함수를 이용해 지정해준 this가 있는지? 보면 된다.

콜백함수가 호출되는 시점에 왼쪽에 객체도 없고, 어플라이도 처리되어 있지 않다.

정답은 window 객체이거나 없다이다.

이제 좀 이해가 되는지? 다음예제를 한번 풀어보자!


var foo = {
    bar : function(){
        console.log(this)
    }
}
var test = function(callback){
    callback();
}
test(foo.bar)

콘솔에는 무엇이 찍힐까?

use strict 환경이 아니라면 window가 찍힌다. 거듭 이야기하지만 this는 호출된 시점만 잘 보면된다.

test함수가 실행될때 콜백함수를 넣어주지만 그 함수가 실행되는 시점은

test 함수내이다. 함수 안을 보면 콜백함수가 실행될때 this를 결정해주는 것이 아무것도 없으므로

전역객체에 연결되는 것이다.

-정리

내가 알고싶었던 this가 정해지는 것은 너무나도 간단했다. 자바스크립트의 설계적 오점을 이해하고, 호출되는 시점에 어떤것이 결정되는지 생각하면 this의 범위를 예측하는건 너무나도 쉬운 것이었다.

this만큼 중요했던 Lexical Environment의 역할은 무엇인지 좀더 생각해보았다.

스코프체인 개념을 이해하기 위함이고, 스코프에 따른 변수를 찾기 위한 방법으로 쓰이는 것이다.

자바같은 언어에서 this와 스코프는 일치했기때문에 자바스크립트의 이러한 특성이 나를 헷갈리게 했던 것이다.

this가 정해주는 것은 this가 가지고 있는 다른 속성값들을 사용하기 위함임을 잘 떠올려야하고,

스코프가 정해주는 것은 내가 가지고 있는 변수들중에 마땅한 것이 없으면 상위 스코프에 찾기 위함임을 잘 떠올린다면 어떤 상황이 오더라도 자바스크립트를 읽어낼 수 있는 능력이 생기는 것이다.

다음 코드는 방금 짠 코드이다.

return new Promise<Object>(function (resolve, reject) {
            that.xhrWithAuth(method, url, true, onsuccess, params);
            console.log(this);
            function onsuccess(error, status, response) {
                if (!error && status == 200) {
                    let fileList = JSON.parse(response);
                    console.log(fileList + "1")
                    result = fileList['files'];
                    console.log(result + "2");
                    resolve(result);
                } else {
                    console.log("Get FileList fail!!!" + error);
                }
            }
        })

정신없이 짜느라 좀 복잡한데, 어떤 함수의 리턴값으로 프로미스 객체를 내보내고 있다.

지금까지 정리한 것을 제대로 이해했다면 저기서 명시된 this가 실제로 어디에 위치하고 있는지 알 수 있을 것이다.

자바스크립트는 함수단위 실행환경이 생기므로 함수단위로 읽어나간다.

추가)

익명함수의 경우 Lexical Environment를 파악하고자 할때는, 익명함수를 매개변수로 삼는 그 함수에 묶이는 것이 아니라, 바로 위 소코프의 멤버로 봐야한다.

다시말하면 익명함수는 이름이 없는 하나의 내부함수라는 것.

Coin Marketplace

STEEM 0.32
TRX 0.12
JST 0.034
BTC 64664.11
ETH 3166.18
USDT 1.00
SBD 4.11