함수 호출 방식에 따른 this 바인딩

future·2021년 1월 12일
0

JavaScript

목록 보기
3/10
post-thumbnail

👉 함수를 호출하는 방식에 따라 this에 바인딩할 객체가 동적으로 결정된다.


① 함수 호출 (기본 바인딩)

// 브라우저
this === window		// true

// 서버 (node.js)
this === global		// true
  • 전역객체는 모든 객체의 유일한 최상위 객체를 의미한다.
var first = 'spring'
console.log(this.first)		// spring

function favouriteWeather() {
  this.second = 'summer';
  console.log(this);		// window
}
console.log(this.second);	// undefined

this.third = 'fall';

console.log(this.third);	// fall
console.log(this.second);	// undefined

favouriteWeather();

console.log(this.third);	// fall
console.log(this.second);	// summer
  • 함수 호출에서 this는 전역 객체(window)를 컨텍스트 객체로 갖는다.

② Method 호출 (암시적 바인딩)

👉 객체의 value 값으로 담긴 함수를 메소드라고 한다.

var b = 200;

function func0() {
  console.log(this.b);
}

const obj = {		
  a: 100,
  func1: func0,
  func2: function() {
    console.log(this.b);
  }
};

obj.func1();	// undefined
obj.func2();	// undefined

const func3 = obj.func1;
func3();	// 200
  • 객체를 통해 함수가 호출될 경우, 해당 객체는 this의 컨텍스트 객체가 된다.
  • obj.func1()에서 func1은 객체 obj를 통해 호출되어서 obj는 this의 컨텍스트 객체가 된다. func1을 호출할 경우 func0이 실행되는데 obj에는 b가 할당되어 있지 않기 때문에 undefined가 출력된다.
  • func1, func2는 객체 obj를 통해 호출되었기 때문에 obj === this가 된다.
  • 전역에서 생성한 변수는 전역 객체에 등록되기 때문에 func3은 window.func3과 같다.
  • func3에서 this의 컨텍스트는 다시 전역 객체인 window가 되기 때문에
    window.func3 === this.func3 === obj.func1이 된다.

③ 생성자 함수 호출 (new 바인딩)

new 키워드를 사용하여 함수를 호출할 경우 만들어진 객체를 인스턴스라고 부른다.

function original(student, time, course) {
  this.student = student;
  this.time = time;
  this.course = course;
}

const fresh1 = new original('john', 'full-time', 'immersive');
console.log(fresh1.student)	// 'john'
console.log(fresh1.time)	// 'full-time'
console.log(fresh1.course)	// 'immersive'

const fresh2 = original('yuno', 'part-time', 'pre');
console.log(fresh2);		// undefined
console.log(fresh2.student);	// TypeError: fresh2 is not a function
  • 새 객체(fresh1)가 만들어지고 호출된 함수(original)과 연결된다.
  • 새로 선언된 객체(fresh1)를 context 객체로 사용하여 함수가 실행된다.
  • 만약 new 키워드 없이 호출할 경우 이는 함수 실행으로 판단되는데, 함수 original에 리턴값이 없기 때문에 undefined를 출력하게 된다.
  • fresh2.studentundefinedstudent라는 속성으로 접근을 하여 생긴 오류인데, 결국엔 undefined.student를 한 셈이 되어 오류를 발생시킨다.

④ call · apply · bind 호출 (명시적 바인딩)

👉 call apply는 함수를 실행, bind는 함수를 리턴한다.

✔️ 이 세 개의 메서드는 첫 번째 인자를 this 값으로 지정한다.

var age = 23;	// global

function func1() {
  console.log(this.age);
}

const mark = { age: 21 };

func1();		// 23
func1.call(mark);	// 21
func1.apply(mark);	// 21

const func2 = func1.bind(mark);
func2();		// 21
  • 첫 번째 인자가 명시적으로 this의 context 객체가 된다.
  • 함수 func1을 호출할 경우, thiswindow이기 때문에 전역 변수를 불러온다.
  • call apply bind를 사용하여 호출할 경우, 첫 번째 인자가 this의 context 객체가 되기 때문에, 인자로 들어온 객체가 this로 바인딩 된다. → this.age === mark.age

✔️ call은 두 번째 인자로는 함수의 매개변수로 전달된다.

func.call(this, ...args)

var obj = {};

function personal(name, job) {
  this.name = name;
  this.job = job;
}

personal.call(obj, 'mark', 'author');
// obj = { name: 'mark', job: 'author' }
  • 첫 번째 인자인 obj는 this의 컨텍스트 객체가 되고, 전달된 인자와 함께 함수를 호출한다.
  • call에서는 두 번째 인자부터 함수의 매개변수에 할당된다. 그러므로 함수 personal의 첫 번째 매개변수(name)에는 두 번째 인자인 'mark'가, 두 번째 매개변수(job)에는 다음 인자인 'author'가 할당된다.
function profile(grade, ...args) {
  return `I am year ${grade} ${this.job}, my major is ${args[0]} & ${args[1]}.`;
}

const me = { job: 'student' };

profile.call(me, 13, 'finance', 'accounting');
// 'I am year 13 student, my major is finance & accounting.'
  • 첫 번째 인자인 methis로 바인딩 된다.
  • call의 두 번째 인자 이후로는 매개변수로 전달되기 때문에 job = student grade = 13이 되고, 그 이후의 인자들은 rest parameter(...args)에 할당된다.

✔️ apply는 두 번째 인자를 배열로 받고 배열 속 요소들을 함수의 매개변수에 할당한다.

func.apply(this, [...args])

var obj = {};

function personal(name, job) {
  this.name = name;
  this.job = job;
}

personal.apply(obj, ['mark', 'author']);
// obj = {name: "mark", job: "author"}
  • 주어진 this 값과 배열로 제공되는 args로 함수를 호출한다.
function profile(name, country, ...args) {
  return `${name} is an ${this.job} from ${country}, and he likes ${args} a lot.`;
}

const him = { job: 'author' };

profile.apply(him, ['mark', 'canada', 'watermelon']);
// 'mark is an author from canada, and he likes watermelon a lot.'
  • apply에서는 첫 번째 인자인 himthis로 바인딩 되며, 두 번째 인자는 배열 형태로 전달된다.
  • 함수의 매개변수에서 rest parameter로 전달되는 인자 또한 두 번째 배열 안에 주어진다.
const numbers = [1, 90, 25, 71, 66];

Math.max(...numbers);			// 90
Math.max.call(null, ...numbers);	// 90
Math.max.apply(null, numbers);		// 90
  • call을 사용할 경우, rest parameter를 사용하여 두 번째 인자에 전달해준다.
  • apply를 사용할 경우, numbers는 배열이기 때문에 그 자체를 두 번째 인자로 전달해준다.

✔️ bind는 새로운 함수를 반환한다.

bind는 함수를 실행하지 않고, this context를 담은 함수를 리턴한다. 그러므로 bind 메서드를 사용한 후에 한 번 더 호출을 해주어야 함수 안에 있는 this를 원하는 객체로 바인딩 할 수 있다.

var binobj = {};

function personal(name, job) {
  this.name = name;
  this.job = job;
}

const other = personal.bind(binobj);
other('mark', 'author');
// binobj = {name: "mark", job: "author"}
  • 첫 번째 인자는 this, 두 번째 인자부터는 필요한 매개변수를 전달한다.
  • 함수에 this 값 객체를 할당해준 뒤, 재호출 시 인자를 추가할 수 있다.
function add(num1, num2) {
  return num1 + num2;
}

const firstResult = add.bind(null, 10, 20);
firstResult;		// 함수 add를 출력
firstResult();		// 30

const secondResult = add.bind(null, 10);
secondResult(20);	// 30
typeof secondResult;	// function
  • bind는 함수를 실행하는 것이 아닌 새로운 함수를 반환하는 메서드이다. 새로 선언된 firstResultsecondResult를 입력할 경우 함수 add를 출력하게 된다.
  • 그러므로 새로 선언된 함수를 실행하기 위해서는 bind 메서드를 사용한 뒤에 한 번 더 호출을 해주어야 한다.
  • 매개변수가 두 개일때 첫 번째 인자만 전달한다고 하여도, 함수 호출 시 두 번째 인자값을 넣어주면 원하는 리턴값을 얻을 수 있다.

❓ 함수와 화살표 함수에서의 this 차이

  • function 키워드를 사용할 경우 ➙ 함수가 어떻게 실행되는지에 따라 결정된다.
  • arrow function을 사용할 경우 ➙ this 값을 상관하지 않는다. ( = this 바인딩을 하지 않음)

👉 그러므로 this를 사용하기 위해서는 키워드 function을 반드시 작성해야 한다.


참고한 글

profile
get, set, go!

0개의 댓글