This

SeungMin·2022년 10월 28일
0

JAVA SCRIPT STUDY

목록 보기
5/9

This?

실행 컨텍스트에서 언급한

VariableEnvironmentLexicalEnvironment에 포함되어있는
ThisBinding이 여기에 해당한다.

This는 전역 공간에서는 전역 객체 자체를 의미한다.

예를들어서
브라우저 환경에서는 Window

console.log(this === window) // true

Node.js 환경이라면 global 인것이다.

console.log(this === global) // true

전역변수의 특이점?

전역변수를 선언하면
자바스크립트 엔진은 이를 전역객체의 프로퍼티로도 할당한다.

변수이자 객체의 프로퍼티인 셈.

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

사실 이러한 동작방식은 당연하다.

자바스크립트의 모든 변수는 특정 개체의 프로퍼티로써 동작하기 때문.

특정 개체란 실행 컨텍스트에서 언급한 LexicalEnvironment이다.

때문에 정확히 말하자면
전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다

그럼 그냥a를 출력해도 출력이 가능한 이유는?

마찬가지로 실행 컨텍스트에서 언급한 이유와 같다.

LexicalEnvironment의 요소인
outerEnvironmentReference의 특성으로 인한 스코프 체인이 발동되면서

a라는 요소를 검색하여 전역 객체를 참조하기 때문에
최종적으로 window.a를 참조하게 되는것이다.

예외사항?

앞서 언급한 내용을 바탕으로는
전역객체에 직접 할당을 하더라도 var로 전역변수를 선언한것과
겉은 방식으로 동작한다는 사실을 알 수 있다.

하지만 예외사항이 있다.

delete

삭제명령은 사용자가 의도치않게 삭제하는 경우를 방지하기 위해서
자바스크립트 엔진에서 마련한 일종의 방어전략에 의하여

delete 명령어가 사용 가능한 경우는
처음부터 전역객체의 프로퍼티로 할당한 경우에 제한한다.

window.a = 3;
delete window.a // true
console.log(a, window.a , this.a) // error

window.b = 3;
delete b // true
console.log(b, window.b, this.b) // error

때문에 전역변수로 할당한 경우의 delete 명령어는 아래와 같이 동작한다.

var a = 3;
delete a // false
console.log(a, window.a, this.a) // 3, 3, 3

var b = 3;
delete window.b // false
console.log(b, window.b, this.b) // 3, 3, 3

메서드 내부에서의 This?

함수호출, 메서드호출

먼저 함수를 어떻게 호출하느냐에 따라서 This 값이 달라진다.

var func = function (x) {
  console.log(this, x)
}

func(1) // (Window { ... } 1

---------------------------------

var obj = {
  method : func
}

obj.method(2) // { method: f } 2

첫번쨰의 경우는 함수를 호출한 방법이다.

두번째의 경우는 메서드를 호출한 방법이다.
메서드는 객체에 할당한 값이 참조하고있는 함수 func를 참조한다.

간단하게 함수앞에 . 콤마가 있는것으로 구분할 수 있다.

그래서 메서드 내부의 this는?

먼저 간단하게 설명하면

위에 기재한 메서드를 구분하는 방법인 .의 앞에 쓰여진 객체가 this가 된다.
호출에 대한 주체이기 때문이다.

var obj = {
  methodA : function () { console.log(this);},
  inner : {
    methodB : function () { consoloe.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(); // 결국은 함수를 호출한것 (Window)
    
    var obj2 = {
      innerMethod : innerFunc
    };
    obj2.innerFunc(); // 호출의 주체인 (obj2)
  }
}
obj1.outer(); // 호출의 주체인 (obj1)

메서드의 내부함수에서의 This를 우회하는 방법?

정말 허무하고 간단한 방법이지만

명시적으로 this를 바인딩 시켜주면 됩니다.


var obj = {
  outer: function() {
    console.log(this);
    var innerFunc1 = function(){
      console.log(this);
    }
  }
  innerFunc1(); // Window

  var self = this;
  var innerFunc1 = function(){
      console.log(this);
  }
  innerFunc2(); // obj
}

This를 바인딩하지 않는 함수?

ES6부터는 함수 내부에서의 this가 전역객체를 바라보는 문제를 해결하고자
화살표 함수를 도입했다.

화살표함수는 실행 컨텍스트를 생성할 때 this바인딩 과정 자체를 생략하여
상위 스코프의 this를 그대로 활용할 수 있다.

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

콜백함수 호출시 그 내부의 This?

콜백함수는 함수A의 제어권을 함수B에 넘겨주게되면 A를 콜백함수라 한다.

이때 this역시 함수B 내부로직에서 정한 규칙에 따라 값이 결정된다.

B또한 함수이기 때문에 기본적으로 전역객체를 참조하지만
B내부에서 별도로 This의 대상을 지정한 경우는 해당 대상을 This로 참조하게됩니다.

setTimeout(function() {console.log(this);},300); // 0.3초뒤 전역객체 출력

[1,2,3,4,5].forEach(function(x){  // 전역객체,각 원소 출력
  console.log(this,x);
});

document.body.innerHTML += `<button id='a'>클릭</button>`;
document.body.querySelector('#a')           
	.addeventListener('click',function(e){
		console.log(this,e);
	})
// querySelector에서 지정한 엘리먼트, 클릭 이벤트에 대한 정보를 클릭시마다 출력

명시적으로 This를 바인딩하는 방법?

call 메서드

call의 첫번째 인자를 This로 바인딩하는 메서드이다.

객체의 메서드를 호출할 때 또한 call을 사용하면
원래의 객체를 참조하지 않고 임의로 지정한 객체를 참조한다.

var func = function(a,b,c){
  console.log(this,a,b,c);
}

func.call({x:1},1,2,3); // {x:1},1,2,3

apply 메서드

기능적으로는 call과 완전히 동일하지만 인자를 받는 방식만 다름.

call은 첫번째 인자를 this로 받고 나머지 인자를 함수의 매개변수로 받지만
apply는 첫번째 인자를 this로 받고 두번째 인자 배열의 원소를 함수의 매개변수로 받음.

var func = function(a,b,c){
  console.log(this,a,b,c);
}

func.call({x:1},1,2,3);   // {x:1},1,2,3
func.apply({x:1},[1,2,3]) // {x:1},1,2,3

유사배열객체에 활용

var obj = {
  0:a,
  1:b,
  2:c,
  length:3
}

Array.prototype.push.call(obj,"d");
console.log(obj);  //{0:a, 1:b, 2:c, 3:d, length:3}

var arr = Array.prototype.slice.call(obj);
console.log(arr); //[a, b, c, d]

obj처럼 키가 인덱스값이며 length 프로퍼티의 값이 0 또는 양의정수인 경우
유사배열객체다.

유사배열객체에는 call 또는 apply 를 사용할 수 있다.

slice는 인자로 시작 인덱스값과 마지막 인덱스 값을 받아서 범위 내의 배열을 반환하지만
인자로 아무값도 넘기지 않으면 원본 배열의 앝은 복사본을 반환한다.

Bind 메서드

bind 메서드는 첫 인자로는 this를 바인딩하고
이후의 인자는 원본 함수의 매개변수로 대치된다.

var func = function(a,b,c,d){
  console.log(this,a,b,c,d);
};
func(1,2,3,4);       // Window{...} 1 2 3 4

var bindFunc1 = func.bind({x:1});

bindFunc1(5,6,7,8);  // {x:1} 5 6 7 8

var bindFunc2 = func.bind({x:2},4,5);

bindFunc2(6,7);      // {x:2} 4 5 6 7
bindFunc2(8,9);      // {x:2} 4 5 8 9

bind된 함수를 새로운 변수에 할당하기 때문에
코드를 추적하기에 더욱 수월하다.

profile
공부기록

0개의 댓글