책/코어 자바스크립트

3-1. this - 상황에 따라 달라지는 this

LAZY SIA 2022. 1. 3. 16:14
728x90
반응형

1-1. 상황에 따라 달라지는 this

전역 공간에서의 this 

- 전역 공간에서의 this는 전역 객체를 의미

- 브라우저 전역 객체 : window

- node.js 환경 전역 객체 : global

 

 

전역 공간에서만 발생하는 특이한 성질

- 전역 변수 선언 시 자바스크립트 엔진은 이를 전역 객체의 프로퍼티로도 할당 (변수이면서 객체의 프로퍼티)

var a = 1;
console.log(a);		// 1
console.log(window.a);	// 1
console.log(this.a);	// 1

- 자바스크립트의 모든 변수는 실은 '특정 객체의 프로퍼티'로서 동작

-> 특정 객체 : 실행 컨텍스트의 LexicalEnvironment. (전역 컨텍스트의 경우, LexicalEnvironment는 전역 객체 그대로 참조)

 

 

전역 변수와 전역 객체 프로퍼티

- 선언시 (같은 결과)

window.a = 1;
console.log(a, window.a, this.a);	// 1 1 1

- 선언 후 삭제시 (전혀 다른 결과)

   (delete 연산자는 window.delete와 같다고 이해하면 됨)

var a = 1;
delete window.a;	// false

var b = 2;
delete b;		// false

window.c = 3;
delete window.c;	// true

window.d = 4;
delete d;		// true

console.log(a, window.a, this.a);	// 1 1 1
console.log(b, window.b, this.b);	// 2 2 2
console.log(c, window.c, this.c);	// Uncaught ReferenceError : c is not defined
console.log(d, window.d, this.d);	// Uncaught ReferenceError : d is not defined

-> 전역 변수로 선언 시, 자바스크립트 엔진이 이를 자동으로 전역 객체 프로퍼티로 할당하며,

     추가적으로 해당 프로퍼티의 configurable(변경 및 삭제 가능성) 속성을 false로 정의하기 때문에 변경 및 삭제를 할 수 없음

 

 

 

메서드로서 호출할 때 그 메서드 내부에서의 this

함수 vs. 메서드

- 앞에 객체가 명시되어 있으면 메서드, 그렇지 않으면 함수

- 독립성에 있어서 서로 차이가 있음

    - 함수 : 그 자체로 독립적인 기능을 수행

    - 메서드 : 자신을 호출한 대상 객체에 관한 동작을 수행

var func = function(x) {
    console.log(this);
};
func();	// window {...}					-- 전역 객체 window

var obj = {
    method : func;
};
obj.mehtod(); // { method : f }				-- 자신을 호출한 객체 obj

-> 익명함수는 그대로인데, 이를 변수에 담아 호출한 경우와 obj 객체의 프로퍼티에 할당해서 호출한 경우의 this가 다름

 

 

메서드 앞에 객체를 명시하는(=메서드를 호출하는) 2가지 방법

1) 객체명.메서드명();

2) 객체명['메서드명']();

 

 

메서드 내부에서의 this

- 메서드 내부에서의 this는 호출한 주체에 대한 정보를 가리킨다

- 메서드로서 호출시 호출주체는 앞에 있는 객체

var obj = {
    methodA : function() {
        console.log(this);
    },
    inner : {
        methodB : function() {
            console.log(this);
        };
    }
}
obj.methodA();		// { methodA : f, inner : {...} }	-- obj
obj.inner.methodB();	// { methodB : f }			-- obj.inner

 

 

 

함수로서 호출할 때 그 함수 내부에서의 this

함수 내부에서의 this

- 함수로서 호출 시 호출주체(객체)를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이라 호출주체 정보 알 수 없음

- 함수에서의 this는 지정하지 않으면 무조건 전역 객체

 

 

메서드 내부함수에서의 this

- 함수로서의 호출인지 메서드로서의 호출인지만 파악하면 됨 (함수인데 지정하지 않았다면 this, 메서드라면 호출한 객체)

var obj1 = {
    outer : function() {
        console.log(this);
        var innerFunc = function() {
            console.log(this);
        }
        innerFunc();	// (2)
        var obj2 = {
            innerMethod : innerFunc;
        };
        obj2.innerMethod();	// (3)
    }
};
obj1.outer();	// (1)

(1) outer() 앞에 obj1.이 있기 때문에 메서드로서의 호출

    { outer : f } ( obj1 바인딩) 출력

(2) innerFunc() 앞에 객체가 없기 때문에 함수로서의 호출

    window {...} (전역 객체 바인딩) 출력

(3) innerMethod() 앞에 obj2.가 있기 때문에 메서드로서의 호출

    { innerMethod : f } ( obj2 바인딩 ) 출력 

 

 

메서드의 내부함수에서의 this를 우회하는 방법 (ES5)

- 호출 주체 없을 경우 자동으로 전역 객체를 바인딩하지 않고 호출 당시 주변 환경의 this를 상속받고 싶을 때는 변수를 활용

var obj = {
    outer : function() {
        console.log(this);
        var innerFunc1 = function() {
            console.log(this);
        }
        innerFunc1();		// window {...}
        
        var self = this;	// 변수 선언 : 상위 스코프의 this를 저장해서 내부함수에서 사용
        var innerFunc2 = function() {
            console.log(this);
        }
        innerFunc2();		// { outer : f }
    }
};
obj.outer();			// { outer : f }

 

 

this를 바인딩하지 않는 함수 (ES6 화살표 함수)

- 함수 내부에서 this가 전역 객체를 바라보는 문제를 보완하기 위해 등장

- 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어 상위 스코프의 this를 그대로 활용 가능

var obj = { 
    outer : function() {
        console.log(this);
        var innerFunc = () => {
            console.log(this);
        };
        innerFunc();	// { outer : f }
    }
};
obj.outer();	// { outer : f }

 

 

 

콜백 함수 호출 시 그 함수 내부에서의 this

콜백함수

- 함수A의 제어권을 다른 함수B(또는 메서드)에게 넘겨주는 경우에서의 함수A를 콜백함수라고 함

- 함수B의 로직에 따라 함수A를 실행하여 this 역시 함수B 내부 로직에 따라 결정됨

 

- 함수에서의 this는 기본적으로 전역 객체를 참조하지만,

   제어권을 받은 함수에서 콜백함수에게 별도로 this가 될 대상을 지정하면 그 대상을 참조함

// (1)
setTimeout( function() {console.log(this);}, 300 );


// (2)
[1,2,3,4,5].forEach( function(x) {consoe.log(this.x);} );


// (3)
document.body.innerText += '<button id="a">클릭</button>';
document.body.querySelect('#a')
    .addEventListener('click', function(e) {
        console.log(this, e);
    });

(1)(2) this 대상 지정하지 않음 -> 전역 객체인 window {...} 출력

    (1) 0.3초 후 콘솔창에 window {...} 출력

    (2) window{...} 와 각 요소를 한 번씩 총 5번 출력

(3) 앞서 지정한 버튼 엘리먼트와 클릭 이벤트에 관한 정보가 담긴 객체 출력

    .addEventLister() 메서드는 기본적으로 콜백함수 호출시 자신의 this를 상속하는 성질이 있음

    따라서 호출된 콜백함수 내부의 this는 .addEventListener()의 this인 document.body.querySelect('#a')인 버튼을 가리킴

-> 콜백함수의 제어권을 가지는 함수(메서드)가 콜백함수에서의 this를 무엇으로 할지 결정하고

     특별히 정의하지 않은 경우 기본적 함수의 this 처럼 전역 객체를 바라봄

 

 

 

생성자 함수 내부에서의 this

생성자 함수

- 공통된 성질을 지니는 객체들을 생성하는데 사용하는 함수

- 자바스크립트에서 생성자는 클래스(class)를 의미하고, 클래스를 통해 만든 객체는 인스턴스(instance)를 의미함

 

- 생성자는 구체적인 인스턴스를 만들기위한 일종의 틀이라고 생각하면 됨

( 미리 준비된 공통 속성에 구체적인 인스턴스의 개성을 더해 개별 인스턴스를 만드는 것임 )

- 자바스크립트는 함수에 생성자 역할을 함께 부여해서, new 명령어로 함수 호출하면 생성자로 동작하게 됨

- 생성자 내부 this는 새로 만들 구체적인 인스턴스 자신을 가리킴

 

 

구체적인 인스턴스 생성과정

- new 명령어로 함수 호출 시 생성자의 prototype 프로퍼티를 참조하는 __proto__라는 프로퍼티가 있는 객체(인스턴스)를 만들고,

   미리 준비된 공통 속성 및 개성을 해당 객체(this)에 부여함

var Cat = function(name, age) {
    this.bark = '야옹';
    this.name = name;
    this.age = age;
};	// 프로퍼티에 각 값을 대입

var choco = new Cat('초코', 7);	// 생성자 함수 내부에서의 this는 생성되는 choco 인스턴스
var nabi = new Cat('나비', 5);	// 생성자 함수 내부에서의 this는 생성되는 nabi 인스턴스

console.log(choco, nabi);

/*
결과
Cat { bark:'야옹', name:'초코', age:7 }
Cat { bark:'야옹', name:'나비', age:5 }
*/

 

반응형