Web-Frontend/JavaScript

[JS] 함수 호출 방식과 this 바인딩

서노리 2023. 10. 3. 22:29
반응형

자바스크립트의 함수는 호출될 때 전달되는 인자값 이외에, arguments 객체와 this를 암묵적으로 전달 받는다. 자바스크립트의 this 키워드는 자바에서의 개념과 달라 혼란을 줄 수 있다.

 

자바에서 this는 인스턴스 자신을 가리키는 참조변수이다. 주로 매개변수와 객체 자신이 가지고 있는 멤버변수명이 같을 경우 이를 구분하기 위해 사용된다.

public Class Person {

  private String name;

  public Person(String name) {
    this.name = name;
  }
}

 

하지만 자바스크립트의 경우 함수 호출 방식에 따라 this에 바인딩되는 객체가 달라진다.

함수 호출 방식은 아래와 같이 다양하다.

1. 함수 호출
2. 메소드 호출
3. 생성자 함수 호출
4. apply/call/bind 호출
var foo = function () {
  console.dir(this);
};

// 1. 함수 호출
foo(); // window
// window.foo();

// 2. 메소드 호출
var obj = { foo: foo };
obj.foo(); // obj

// 3. 생성자 함수 호출
var instance = new foo(); // instance

// 4. apply/call/bind 호출
var bar = { name: 'bar' };
foo.call(bar);   // bar
foo.apply(bar);  // bar
foo.bind(bar)(); // bar

1. 함수 호출

이 경우 this는 전역객체에 바인딩된다. 전역객체는 모든 객체의 유일한 최상위 객체를 의미하며 일반적으로 browser-side에서는 window, server-side(Node.js)에서는 global 객체를 의미한다. 

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

전역함수는 물론이고 심지어 내부함수의 경우에도 this는 외부함수가 아닌 전역객체에 바인딩된다.

 

2. 메소드 호출

함수가 객체의 프로퍼티 값이면 메소드로서 호출된다. 이때 메소드 내부의 this는 해당 메소드를 소유한 객체, 즉 해당 메소드를 호출한 객체에 바인딩된다.

var obj1 = {
  name: 'Lee',
  sayName: function() {
    console.log(this.name);
  }
}

var obj2 = {
  name: 'Kim'
}

obj2.sayName = obj1.sayName;

obj1.sayName(); // Lee
obj2.sayName(); // Kim

 

3. 생성자 함수 호출

자바스크립트의 생성자 함수는 객체를 생성하는 역할을 한다. 하지만 자바와 같은 객체지향 언어의 생성자 함수와는 다르게 그 형식이 정해져있는 것이 아니라 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.

// 생성자 함수
function Person(name) {
  this.name = name;
}

var me = new Person('Lee');
console.log(me); // Person {name: "Lee"}

// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person('Kim');
console.log(you); // undefined

 

new 연산자와 함께 생성하면 this는 해당 객체로 바인딩된다.

 

4. apply/call/bind 호출

apply, call, bind와 같은 명시적 바인딩을 사용했을 때는 this가 해당 메소드에 인자로 전달된 객체에 바인딩된다.

function func() {
  console.log(this.name);
}

var obj = { name: 'obj name' };
func.call(obj); // obj name
func.apply(obj); // obj name
(func.bind(obj))(); // obj name

 

반응형