this와 함수

yunji_kim·2023년 3월 23일
0

JavaScript

목록 보기
2/9

this란?

  • this는 현재 실행 중인 함수 내에서 현재 실행 컨텍스트의 객체를 참조하는 특별한 키워드이다.
  • 함수마다 하나의 this를 가질 수 있으며, this에는 어떤 값이든 들어갈 수 있다.
  • this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다.

함수호출

this는 함수가 호출되는 방식에 따라 값이 달라질 수 있습니다. 함수가 호출되는 상황은 4가지가 있다.

호출 방식this가 가리키는 값 (this 바인딩)
일반 함수로서 호출전역 객체
메서드로서 호출메서드를 호출한 객체(마침표 앞의 객체)
생성자 함수로서 호출생성자 함수가 생성할 인스턴스 객체
  1. 함수 호출 - 함수를 직접 호출
  2. 메서드 호출 - 객체의 메서드 호출
  3. 생성자 호출 - 생성자 함수를 호출
  4. 간접 호출 - call, apply 등으로 함수를 간접 호출

위와 같은 환경에 따라 this가 바뀌는 것을 동적 바인딩(Dynamic binding)이라고 한다.

this 조작

  • bind, apply, call 등으로 this가 가리키는 것을 조작할 수 있다.
  • 일반적으로 setTimeout함수에 전달된 함수 내부에서 this키워드는 전역 객체(window)를 가리키게 된다. 하지만, 화살표 함수를 사용해서 화살표 함수가 정의된 컨텍스트를 가리키게 할 수 있다.

this 호출 예시

This는 호출되는 방식에 따라서 바인딩 대상이 달라집니다. 바인딩이라는 단어가 어렵다면 가리키는 객체라고 생각해보세요.

함수에서 사용되는 This

전역 컨텍스트

기본적으로 전역 컨텍스트에서 this는 window를 가리킵니다. 따라서 전역 렉시컬 환경에 있는 name도 접근할 수 있습니다.

const name = "elice";
console.log(this);// window {...}

일반 함수 호출

일반 함수 호출에서도 this는 window를 가리킵니다. 따라서 myFunc 함수 내부에 있는 name에 접근 할 수 없습니다.

function myFunc() {
  const name = "elice";
  console.log(this);
}

myFunc();// 일반 함수 호출시 window {...}

생성자 함수 호출

생성자 함수 내부의 this는 new 키워드를 통해 앞으로 만들어질 인스턴스 객체를 가리킵니다.

생성자 함수는 함수 이름으로 된 객체를 만들어줍니다. 반환해야할 것들은 this 즉, 생성될 시점의 컨텍스트에 저장되고 자동으로 return 되기 때문에 명시적으로 써줄 필요가 없습니다. 따라서 다른 값을 return 하면 객체가 만들어지지 않습니다.

function myFunc(name) {
  this.name = name;
  this.getName = function () {
    console.log("getName this:", this);
    return this.name;
  };

  console.log("myFunc this:", this);
// return this; 생략 가능합니다.
}

const o = new myFunc("elice");// myFunc this: myFunc {...}
o.getName()// myFunc this: myFunc {...}

결과적으로 일반적으로 함수를 호출할 때는 this가 모두 window를 가리킵니다. 함수를 객체로 생성하는 방법에만 this가 생성하는 함수 내부를 가리킵니다.

객체에서 사용되는 this

객체에서의 this는 메소드를 호출한 객체를 가리킵니다.

const o = {
  name: "elice",
  myFunc: function () {
    console.log(this);
  },
};

o.myFunc();// {name: 'elice', myFunc: ƒ}

하지만 객체 안 함수의 내부 함수에 This는 동적바인딩으로 인해 전역 객체를 가리킵니다.

const o = {
  name: "elice",
  myFunc: function () {
    return function () { console.log(this) }
  },
};

o.myFunc()();// window {...}

콜백 함수의 this

  • <다른 함수의 인자로 전달되어 호출될 때>
    이 경우, this는 전역 객체를 가리키게 됩니다. 이는 콜백 함수가 호출되는 동안 함수 호출의 주체가 명확하지 않기 때문입니다.
  • <메소드에서 콜백 함수를 호출할 때>
    그러나, 콜백 함수를 호출하는 함수가 메소드로 정의되어 있고 해당 메소드에서 콜백 함수를 호출할 때, this는 해당 메소드를 소유하고 있는 객체를 가리킵니다.

This 고정하기

1. () ⇒ 화살표 함수로 고정하기

일반 함수로 호출했을 경우

생성자 함수로 만든 객체, 객체 리터럴 방식으로 만든 객체 모두 내부에서 this가 자기 자신을 가리키고 있습니다. 하지만 두 경우 모두 내부에서 함수를 만들고 해당 함수에서 this를 출력하면 window를 가리킵니다. 다음 코드를 살펴봅시다.

/* 생성자 함수 방식 */
function createObject() {
  this.myFunc = function () {
    console.log("myFunc this:", this);
    return function () { console.log("myFunc return this:", this) };
  };
}

const o = new createObject();
o.myFunc()();
// myFunc        this: createObject {...}
// myFunc return this: window {...}

/* 객체 리터럴 방식 */
const o = {
  myFunc: function () {
    console.log("myFunc this:", this)
    return function () { console.log("myFunc return this:", this) }
  },
};

o.myFunc()();
// myFunc        this: myFunc {...}
// myFunc return this: window {...}

객체의 최상위 스코프에서의 함수 본문 this는 객체 최상위 스코프를 정상적으로 가리킵니다. 하지만 함수 안에 또다시 함수가 리턴되는 경우에는 window 객체를 가리킵니다.

화살표 함수로 호출했을 경우

다음과 같이 화살표 함수를 사용하면 객체 최상위 스코프를 가리키도록 유지합니다.

/* 생성자 함수 방식 */
function createObject() {
  this.myFunc = function () {
    console.log("myFunc this:", this);
		return () => { console.log("myFunc return this:", this) };
  };
}

const o = new createObject();
o.myFunc()();
// myFunc        this: createObject {...}
// myFunc return this: createObject {...}

/* 객체 리터럴 방식 */
const o = {
  myFunc: function () {
    console.log("myFunc this:", this);
		return () => { console.log("myFunc return this:", this) };
  },
};

o.myFunc()();
// myFunc        this: myFunc {...}
// myFunc return this: myFunc {...}

2. call, apply, bind 메서드로 고정하기

앞선 상황과 동일한 코드를 가지고 원하는 객체를 가리키도록 해보겠습니다.

bind 메서드

/* 생성자 함수 방식 */
function createObject() {
  this.myFunc = function () {
    console.log("myFunc this:", this);
    return function () { console.log("myFunc return this:", this) };
  };
}

const o = new createObject();
o.myFunc().bind(o)();// bind 메서드를 사용해 o객체로 고정시킵니다.

// myFunc        this: createObject {...}
// myFunc return this: createObject {...}

/* 객체 리터럴 방식 */
const o = {
  myFunc: function () {
    console.log("myFunc this:", this)
    return function () { console.log("myFunc return this:", this) }
  },
};

o.myFunc().bind(o)();// bind 메서드를 사용해 o객체로 고정 그리고 함수를 실행

// myFunc        this: myFunc {...}
// myFunc return this: myFunc {...}

apply, call 메서드

두 메서드에 차이는 call 메서드는 인수 목록을 받고, apply 메서드는 인수 배열을 하나 받습니다.

ex) call(this, var1, var2, var3, …) / apply(this, [ el, el2, el3, … ])

/* 생성자 함수 방식 */
function createObject() {
  this.myFunc = function () {
    console.log("myFunc this:", this);
    return function () { console.log("myFunc return this:", this) };
  };
}

const o = new createObject();
o.myFunc().call(o, null);// call  메서드를 사용해 o객체로 고정시킨 후 함수 실행

// myFunc        this: createObject {...}
// myFunc return this: createObject {...}

o.myFunc().apply(o, null);// apply 메서드를 사용해 o객체로 고정시킨 후 함수 실행

// myFunc        this: createObject {...}
// myFunc return this: createObject {...}

/* 객체 리터럴 방식 */
const o = {
  myFunc: function () {
    console.log("myFunc this:", this)
    return function () { console.log("myFunc return this:", this) }
  },
};

o.myFunc().call(o, null);// call  메서드를 사용해 o객체로 고정시킨 후 함수 실행

// myFunc        this: myFunc {...}
// myFunc return this: myFunc {...}

o.myFunc().apply(o, null);// apply 메서드를 사용해 o객체로 고정시킨 후 함수 실행

// myFunc        this: myFunc {...}
// myFunc return this: myFunc {...}

call, apply, bind는 일반 함수 호출에도 원하는 객체를 가리킬 수 있습니다.

const o = {
    name: "Elice",
}

function myFunc() {
  console.log(this);
}

myFunc()// window {...}
myFunc.bind(o)()// {name: 'Elice'}
myFunc.call(o, null)// {name: 'Elice'}
myFunc.apply(o, null)// {name: 'Elice'}

화살표 함수와 일반 함수의 this

<일반 함수의 this>
새롭게 생성된 실행 컨텍스트를 기리킨다.

<화살표 함수의 this>
호출된 함수를 둘러싼 실행 컨텍스트를 가리킨다.

  • 화살표 함수의 this는 정해지면 바꿀 수 없다. call, bind, apply를 사용해도 바뀌지 않는다.
  • setTimeout을 사용했을 때와 같이 this가 바뀔 수 있는 상황에서는 유용하다. 반대로, this를 조작해야할 경우 일반함수를 사용해야한다.
profile
| FE Developer | 기록의 힘 |

0개의 댓글