Post

this 바인딩

this 바인딩

자바스크립트에서 this가 참조되는것은 호출에따라 달라지는데 이것을 this 바인딩이라고 한다

1
2
3
4
5
6
function foo() {
  const a = 10;
  console.log(this.a);
}

foo(); 

실행 결과값이 10이 나올줄 알았지만 console에 보이는 값은 undefined 이다

this가 어디에 바인딩되는지 알기위해서 4가지 규칙을 알면 헷갈리지 않고 this를 알 수 있다

 

 

기본 바인딩

기본 바인딩은 나머지 4가지 규칙에 포함되지 않는 경우 기본값으로 실행되는 바인딩이다 이때 this는 전역 객체에 바인딩된다 (브라우저면 window node.js이면 global)

 

1
2
3
4
5
6
function foo() {
  const a = 10;
  console.log(this.a);
}

foo(); //undefined

여기서는 window에 a라는 프로퍼티가 없어서 undefined가 출력 되었다

 

1
2
3
4
5
6
7
window.a=100
function foo() {
  const a = 10;
  console.log(this.a);
}

foo();//100

this.a는 전역 객체의 a를 의미하기 때문에 10이 출력되지 않고 100이 출력된다

 

1
2
3
4
5
6
7
8
'use strict'
window.a = 20;

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

foo(); // Uncaught TypeError: Cannot read properties of undefined (reading 'a')

엄격모드에서는 기본 바인딩 대상에서 전역 객체는 제외된다 전역객체를 참조하면 그 값은 undefined

 

 

암시적 바인딩

함수가 객체의 메서드로 호출될 때 this가 바인딩 되는것이다 이때 this는 해당 함수를 호출한 객체 컨텍스트 객체에 바인딩된다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function foo() {
    console.log(this.name);
}

var person = {
    name: '철수',
    foo: foo
};

person.foo(); //철수 여기서 person에 바인딩 된다

const foo = {
  a: 20,
  bar: function () {
    console.log(this.a);
  }
}

foo.bar(); // 20 foo에 바인딩된다

해당 함수를 호출한 객체 person과 foo에 바인딩 된다

 

암시적 소실

암시적 바인딩을 사용할때 일어날 수 있는 문제점은 foo.bar를 매개변수로 넘겨서 실행할 때 생긴다

1
2
3
4
5
6
7
8
const foo = {
		a: 20,
		bar: function () {
		  console.log(this.a);
		}
	  }
	  
setTimeout(foo.bar, 1000);//undefined

setTimeout 함수에 foo.bar가 매개변수로 들어간다 1초뒤에 콘솔에 20이 나올 것이라고 예상했다 하지만 undefined가 출력 되었다

암시적 바인딩이 적용된 콜백함수를 인자로 넘긴다고 해도 위 setTimeout에 전달한 콜백은 bar라는 레퍼런스이다 따라서 foo 컨텍스트 객체의 정보를 가지지 않기 때문에 암시적 바인딩이 소실되었고 그래서 기본 바인딩이 적용되었다

전역 변수에 bar라는 프로퍼티가 없어서 undefined가 출력되었다

 

 

명시적 바인딩

자바스크립트의 모든 함수는 call(), apply(), bind()를 메서드로 가지고 있다 이 세개중 하나를 호출해서 this바인딩을 코드에 명시하는것을 명시적 바인딩이라고 한다

call(), apply()

1
2
3
4
5
6
7
8
9
10
const foo = {
		a: 20
}
	  
function bar() {
  console.log(this.a);
}

bar.call(foo); // 20
bar.apply(foo); // 20

bar에서 사용된 this는 foo에 명시적으로 바인딩 된다 컨텍스트가 foo가 된다

call과 apply는 func.call(인자)에서 인자는 this에 넣고 싶은 객체를 넣어 this를 첫번째 인자로 바꾸어 호출하는것과 같다

call과 apply는 두번째 매개변수로 객체의 인자를 전달하는데 call은 매개변수의 목록, apply는 매개변수의 배열을 받는다

 

bind()

1
2
3
4
5
6
7
8
9
10
11
const foo = {
  a: 20,
}

function bar() {
  console.log(this.a);
}

const bound = bar.bind(foo)

bound(); // 20

bind메서드를 이용하면 this가 바인딩된 함수를 반환한다 이를 하드 바인딩이라고 하고 이후 호출 될때마다 처음 정해진 this바인딩을 가지고 호출된다

bind() 기본 문법 let boundFunc = func.bind(context);

func.bind(context)는 함수처럼 호출가능한 특수 객체를 반환한다 이 객체를 호출하면 this가 context로 고정된 함수가 반환된다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function foo() {
		console.log(this.a);
		console.log(this.b);
	}
	foo(); //undefined 기본 바인딩

	const num = {
		a:30,
		b:'haha'
	}

	var bindTest = foo.bind(num);
	bindTest();//30,haha
	console.log(typeof(bindTest)); //function
	

call이나 apply는 함수를 호출하는 함수이고 bind는 함수를 리턴한다

1
2
3
4
5
6
7
8
9
10
11
function foo() {
  console.log(this.a);
}
const num = {
  a:30
}

var bindTest = foo.bind(num);
bindTest();//30
var callTest = foo.call(num);//30
console.log(callTest); //undefined

따라서 callTest는 리턴이 없어서 undefined가 출력되지만(예상) bindTest에서는 함수를 리턴하기 때문에 변수 bindTest에 넣고 고정된 this로 호출이 가능하다

 

 

new 바인딩

new 키워드는 함수를 호출할 때 객체를 초기화시킨다 이 때 사용되는 함수를 생성자 함수라고 한다(생성자 함수는 대문자시작)

new로 선언할 경우 this는 전역 객체가 아닌 생성된 객체 자체를 가리키게 된다

1
2
3
4
5
6
7
8
9
function Person(name) { //생성자 함수
		this.name = name;
}
	
	
var obj1 = new Person('chris');
console.log(obj1);//Person {name: 'chris'}
var obj2 = new Person('tom');
console.log(obj2);//Person {name: 'tom'}

Person함수가 new로 호출될때 새로운 객체가 생성되고 새로 생성된 객체가 this 바인딩된다 obj라는 객체에 name 프로퍼티 저장되고 객체가 생성 완료된다

 

의문

1
2
3
4
5
6
7
8
9
10
11
let obj3 = {
		name: "kago"
};
console.log(obj3); //{name: 'kago'}
function Person(name) { //생성자 함수
  this.name = name;
}
var obj1 = new Person('chris');
console.log(obj1);//Person {name: 'chris'}
var obj2 = new Person('tom');
console.log(obj2);//Person {name: 'tom'}

Person 생성자 함수이름이 앞에 붙는것에 의미가 있나? obj3나 obj2,1이 같다고 생각해서 콘솔 출력해봤는데

그냥 생성자 함수를 표기해준게 끝인가?

 

바인딩 순서

  1. new 바인딩
  2. 명시적 바인딩
  3. 암시적 바인딩
  4. 기본 바인딩

 

 

this 무시

명시적 바인딩이 이루어질때 call(), apply(), bind()의 첫번째 인자로 null, undefined가 입력되면 명시적 바인딩이 무시되고 기본 바인딩이 이루어진다

 

 

화살표 함수

일반적으로 함수들은 4가지 규칙을 준수하지만 ES6부터 규칙을 따르지 않는 화살표 함수가 등장 했다

그냥 함수의 this를 쓴다면 기본 바인딩에 의해서 전역 변수가 호출된다

하지만 화살표 함수를 이용하면 그 함수를 호출한 배경이 this에 담을 수 있다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const account1 = {
		username: "minnnning",
		age: 11,
		getAge: function() {
		  innerFunc = function() {  return `${this.username}님의 나이는 ${this.age}입니다.`;}
		  console.log(innerFunc());
		}
	  };
account1.getAge(); //undefined님의 나이는 undefined입니다. 전역 객체를 가리킴

	const account2 = {
		username: "minnnning",
		age: 20,
		getAge: function() {
		  innerFunc = () => `${this.username}님의 나이는 ${this.age}입니다.`;
		  console.log(innerFunc());
		}
	  };
account2.getAge(); //minnnning님의 나이는 20입니다.

innerFunc에서 화살표 함수를 사용하면 this는 전역객체를 가리키지 않고 화살표 함수는 자신만의 this를 가지지 않기 때문에 바깥 범위에서 this의 값을 계승 받는다

화살표 함수의 렉시컬 바인딩(화살표 함수의 바인딩)은 절대 오버라이드(상위 클래스의 메서드를 재정의 하는 것) 될 수 없다 따라서 apply, bind등의 함수나 new 함수로 오버라이드할 수 없다

this를 확실하게 보장하기 때문에 콜백 함수로 많이 이용된다

  • 화살표함수는 익명 함수로만 만들 수 있다
  • 생성자로 사용할 수 없다
  • 스스로의 this와 argument를 가지지 않는다
  • 함수가 정의된 범위에 존재하는 this를 가리킨다
  • 화살표 함수는 생성될 때 this가 결정된다

렉시컬 스코프(함수를 어디서 선언하였는지에 따라 상위 스코프를 결정하는 것이다. 중요한 점은 함수의 호출이 아니라 함수의 선언에 따라 결정된다)

1
2
3
4
5
6
7
8
9
10
11
12
13
var num = 1;

function a() {
  var num = 10;
  b();
}

function b() {
  console.log(num);
}

a();//1
b();//1

선언 위치 차이

1
2
3
4
5
6
7
8
9
10
11
12
13
var num = 1;

function a() {
	var num = 10;

  function b() {
    console.log(num);
  }
  b();

}

a();//10

자바스크립트에서는 함수를 선언하고 해당 범위에 없다면 자신보다 상위에서 찾게 된다

This post is licensed under CC BY 4.0 by the author.