본문 바로가기

공부/javascript

javascript 심화 3 (this, 화살표함수, call, apply, bind)

반응형

 

this 📖

객체지향 언어에서의 this는 클래스로 생성한 인스턴스를 말합니다. 그러나 자바스크립트에서의 this는 `자신이 속한 객체` 또는 `자신이 생성할 인스턴스`를 가리키는 자기 참조 변수입니다. 즉 this를 통해 객체나 인스턴스의 프로퍼티나 메서드를 참조할 수 있습니다. 

 

this는 언제 결정 될까?🧐

this는 `함수를 호출할 때 결정`됩니다. 즉 동적으로 결정된다는 것입니다.

 

브라우저에서에 this는 Window객체를 의미합니다.

브라우저에서의 this

 

node 환경에서의 this는 global객체를 의미합니다.

node환경에서의 this

 

그러나 함수를 호출하는 방식에 따라 바인딩 객체가 달라집니다.

 

일반 함수 호출에서의 this ➡️ 전역 객체

일반 함수에서의 this는 전역 객체를 의미하기 때문에, 브라우저에서는 `window`, node 환경에서는 `global`객체를 가리킵니다.

 

함수 내부에서의 this는 호출 주체를 알 수 없기 때문에 this는 지정되지 않습니다. 그래서 this가 지정되지 않는 경우 전역 객체를 의미합니다. 따라서 독립적으로 호출할 땐 this는 항상 전역객체를 가리킵니다.

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

function foo() {
  console.log('foo: ', this); // window
  function bar() {
    console.log('bar: ', this); // window
  }
}

foo();

 

 

그럼 전역 객체를 사용하고 싶지 않으면 어떻게 해야할까요?

call, apply, bind를 사용하면 바인딩할 객체를 명시적으로 정할 수 있습니다. (밑에서 자세하게 설명해 드릴게요~!) 👍

 

메서드 호출에서의 this ➡️ 메서드를 호출한 객체

✨점(.)으로 호출하든, 대괄호([])로 호출하든 결과는 같습니다!✨

 

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

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

obj.methodA();             // this === obj
obj['methodA']();          // this === obj

obj.inner.methodB();       // this === obj.inner
obj.inner['methodB']();    // this === obj.inner
obj['inner'].methodB();    // this === obj.inner
obj['inner']['methodB'](); // this === obj.inner

 

 

그러나 메서드 내부라고 해도, 함수로서 호출한다면 this는 전역 객체를 의미합니다.❗

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

    var obj2 = {
      innerMethod: innerFunc
    };
    obj2.innerMethod();
  }
};
obj1.outer();

실행 결과 ✨

  • 1번은 obj1.outer() 메서드를 실행했기 때문에 this는 obj1가 됩니다.
  • 2번은 innerFunc()를 호출할 때 메서드가 아닌 그냥 함수 호출이기 때문에 this는 전역 객체가 됩니다.
  • 3번은 obj2.innerMethod() 메서드를 실행했기 때문에 this는 obj2가 됩니다.

 

 

근데 개발자 입장에서는 이해하기 쉽지 않을 수 있습니다. 그래서 this를 우회할 수 있는 방법을 알려드리겠습니다.😊

 

변수를 활용하는 방법❗

내부 스코프에 이미 존재하는 this를 별도에 변수에 할당하는 방법!

var obj1 = {
  outer: function() {
    console.log(this); // (1) outer

    // AS-IS
    var innerFunc1 = function() {
      console.log(this); // (2) 전역객체
    }
    innerFunc1();

    // TO-BE
    var self = this;
    var innerFunc2 = function() {
      console.log(self); // (3) outer
    };
    innerFunc2();
  }
};

// 메서드 호출 부분
obj1.outer();

 

여기서 this는 outer 스코프 안에 있는 this이기 때문에 obj1을 가리킵니다. 그래서 self를 출력하게 되면 obj1이 출력이 됩니다!

var self = this;

 

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

일반 함수와 화살표 함수의 큰 차이점은 `this binding` 여부입니다.

 

`일반 함수` : 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정됩니다.

`화살표 함수` : 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정됩니다. 그리고 언제나 상위 스코프의 this를 가리킵니다.

 

정적으로 결정된다는 의미함수가 정의된 위치에 따라 this가 고정돼서 변하지 않는다는 뜻입니다. 일반 함수와는 달리, 어떻게 호출되든 this가 항상 동일하다는 의미입니다.

 

 

생성자 함수 호출에서의 this ➡️ 생성할 인스턴스

 

`생성자` : 인스턴스를 만들기 위한 일종의 틀 ex) 붕어빵 틀

생성자 함수에서의 this는 새로 생성되는 객차 자신을 가리킵니다.

 

쉽게 설명하면 `new Cat('초코', 7)`을 사용하면 this는 새로 만들어진 `choco`라는 객체를 가리킵니다.

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

 

 

call / apply / bind 호출에서의 this 📖

call

call 메서드는 인자를 배열로 넘기지 않고 `요소 하나씩` 넘깁니다.

 

함수를 실행하고 첫 번째 인자로 전달한 값에 this를 바인딩합니다.

var obj = {
  a: 1,
  method: function (x, y) {
    console.log(this.a, x, y);
  }
};

obj.method(2, 3); // 1 2 3
obj.method.call({ a: 4 }, 5, 6); // 4 5 6

 

 

여기서 `obj.method.call({ a: 4 }, 5, 6)`으로 호출했기 때문에 this는 {a: 4}를 가리키게 됩니다!

obj.method.call({ a: 4 }, 5, 6); // this = { a: 4 }

 

apply

apply 메서드는 인자를 `배열`의 형태로 전달합니다.

 

함수를 실행하고 첫 번째 인자로 전달한 값에 this를 바인딩합니다.

var obj = {
  a: 1,
  method: function (x, y) {
    console.log(this.a, x, y);
  }
};

obj.method.apply({ a: 4 }, [5, 6]);

 

 

call / apply 활용

아래 코드에서 Student, Employee는 Person의 속성을 받고 싶어 하기 때문에 call 메서드를 사용해 `Person`의 생성자를 각각 `Student`와 `Employee`의 인스턴스에 적용하는 것입니다!

function Person(name, gender) {
  this.name = name;
  this.gender = gender;
}
function Student(name, gender, school) {
  Person.call(this, name, gender); // 여기서 this는 student 인스턴스!
  this.school = school;
}
function Employee(name, gender, company) {
  Person.apply(this, [name, gender]); // 여기서 this는 employee 인스턴스!
  this.company = company;
}
var kd = new Student('길동', 'male', '서울대');
var ks = new Employee('길순', 'female', '삼성');

 

call 메서드는 `Person`생성자 함수를 `Student``Employee`같은 다른 객체에 적용해, 그 객체들이 `Person`의 속성을 물려받을 수 있게 해 줍니다.

 

쉽게 설명하면 `call`메서드는 어떤 함수(=Person)에서 this를 다른 객체(Student)로 바꿔서 그 함수 실행하게 해주는 방법입니다!

 

 

bind

 apply와 call 메서드와의 차이점은 함수를 실행하지 않고 `새로운 함수를 반환` 합니다.

 

함수를 실행하고 첫 번째 인자로 전달한 값에 this를 바인딩합니다.

그리고 this를 함수에 미리 적용(고정)할 수 있고 반환된 함수를 실행해야 원본 함수가 실행됩니다.

// 함수에 this 미리 적용
var bindFunc1 = func.bind({ x: 1 }); // 바로 호출되지는 않아요! 그 외에는 같아요.
bindFunc1(5, 6, 7, 8); // { x: 1 } 5 6 7 8

// 부분 적용 함수 구현
var bindFunc2 = func.bind({ x: 1 }, 4, 5); // 4와 5를 미리 적용
bindFunc2(6, 7); // { x: 1 } 4 5 6 7
bindFunc2(8, 9); // { x: 1 } 4 5 8 9

 

 

아래 예제를 보면 bind 메서드를 사용하게 되면 innerFunc의 this를 obj로 바인딩하게 됩니다.

var obj = {
  outer: function() {
    console.log(this);
    var innerFunc = function () {
      console.log(this);
    }.bind(this); // innerFunc에 this를 결합한 새로운 함수를 할당
    innerFunc();
  }
};
obj.outer();

 

요약 📖

  • this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수이기 때문에 객체나 인스턴스의 프로퍼티나 메서드를 참조 가능
  • 일반 함수 this는 함수를 호출할 때 결정 되기 때문에 동적으로 결정되고, 화살표 함수는 정적으로 결정됨.
  • 일반함수(=독립적으로 수행하는 모듈)에서 this ➡️ 전역 객체
  • 메서드에서 this ➡️ 메서드를 호출한 객체
  • 생성자에서 this ➡️ 생성할 인스턴스
  • this 우회 방법
    • 변수를 활용하는 방법 ex) var self = this;
    • 화살표 함수를 활용하는 방법 ex) () => {};
  • call, apply, bind는 첫 번째 인자로 전달한 값에 this를 바인딩함.
    • call은 인자 하나씩 넘기고 apply는 배열로 넘김 그리고 함수를 즉시 실행함. ex) Person에 this를 Student에 인스턴스로 바꿔줌 그래서 Person 속성을 Student 속성으로 사용할 수 있음.
    • bind는 this를 함수에 미리 적용, 함수를 즉시 실행하지 않고 새로운 함수를 반환.

 

안뇽~😄


출처 🏷️

https://enjoydev.life/blog/javascript/5-this

 

반응형