자바스크립트의 함수는 호출될 때, 매개변수로 전달되는 인자값 이외에, arguments 객체와 this 를 암묵적으로 전달 받는다.

해당 함수 호출 방식에 의해 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. 함수 호출

전역객체는 일반적으로 Browser-side에서는 window, Server-side(Node.js)에서는 global 객체를 의미한다.
전역객체는 전역 스코프를 갖는 전역변수를 property로 소유한다.
기본적으로 this는 전역객체에 바인딩 되며, 내부함수는 일반 함수, 메소드, 콜백함수 어디에서 선언되었든 관계없이 전역객체를 바인딩한다.

// in browser console
this === window // true

// in Terminal
node
this === global // true

2. 메소드 호출

함수가 객체의 property 값이면 메소드로써 호출 되어, 해당 메소드를 소유한 객체에 바인딩 된다.

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 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.
new 연산자와 함께 생성자 함수를 호출하면 this바인딩이 메소드나 함수 호출 때와는 다르게 동작한다.

  • 빈 객체 생성 및 this 바인딩: 생성자 함수의 코드가 실행되기 전 빈 객체가 생성된다. 이후 생성자 함수 내에서 사용되는 this는 이 빈 객체를 가리킨다.
  • this를 통한 프로퍼티 생성: 생성된 빈 객체에 this를 사용하여 동적으로 프로퍼티나 메소드를 생성할 수 있다.
  • 생성된 객체 반환:
    • 반환문이 없는 경우, this에 바인딩 된 새로 생성한 객체가 반환된다.
    • 반환문이 this가 아닌 다른 객체를 명시적으로 반환하는 경우, this가 아닌 해당 객체가 반환된다.
function Person(name) {
  // 생성자 함수 코드 실행 전 -------- 1
  this.name = name;  // --------- 2
  // 생성된 함수 반환 -------------- 3
}

var me = new Person('Lee');
console.log(me.name);

4. apply / call / bind 호출

apply() 메소드는 this를 특정 객체에 바인딩할 뿐 본질적인 기능은 함수 호출이다.

var value = 1;

var obj = {
  value: 100,
  foo: function() {
    console.log("foo's this: ",  this);  // obj
    console.log("foo's this.value: ",  this.value); // 100
    function bar(a, b) {
      console.log("bar's this: ",  this); // obj
      console.log("bar's this.value: ", this.value); // 100
      console.log("bar's arguments: ", arguments);
    }
    bar.apply(obj, [1, 2]);
    bar.call(obj, 1, 2);
    bar.bind(obj)(1, 2);
  }
};

obj.foo();

apply()와 call() 메소드는 콜백 함수의 this를 위해서 사용되기도 한다.

function Person(name) {
  this.name = name;
}

Person.prototype.doSomething = function(callback) {
  if(typeof callback == 'function') {
    // --------- 1: this === Person
    callback();
  }
};

function foo() {
  console.log(this.name); // --------- 2: this === window
}

var p = new Person('Lee');
p.doSomething(foo);  // undefined
function Person(name) {
  this.name = name;
}

Person.prototype.doSomething = function (callback) {
  if (typeof callback == 'function') {
    callback.call(this);
  }
};

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

var p = new Person('Lee');
p.doSomething(foo);  // 'Lee'

Function.prototype.bind는 함수에 인자로 전달한 this가 바인딩된 새로운 함수를 리턴한다. 즉, Function.prototype.bind는 Function.prototype.apply, Function.prototype.call 메소드와 같이 함수를 실행하지 않기 때문에 명시적으로 함수를 호출할 필요가 있다.

참고자료

https://poiemaweb.com/js-this