[F-Lab 모각코 챌린지 3일차] This 바인딩(1)

Nami·2023년 6월 3일
0

66일 포스팅 챌린지

목록 보기
3/66

실행 컨텍스트에 대한 정리를 하다 this 바인딩 부분도 공부할 것이 많아 따로 정리하고싶었다.
(1)에서는 this의 정의와 쓰이게 된 이유, 일반 함수 호출, 메서드 호출, 생성자 함수 호출 등 상황별로 어떤 값이 this에 바인딩 되는지를 알아볼 것이다.
(2)에서는 규칙을 깨고 별도의 대상을 명시적으로 this에 바인딩하는 방법에 대해 알아볼 것이다.


this

객체는 상태 state를 나타내는 프로퍼티와 동작 behavior을 나타내는 메서드를 하나의 논리적인 단위로 묶은 복합적인 자료구조다.

동작을 나타내는 메서드는 자신이 속한 객체의 상태, 프로퍼티를 참조하고 변경할 수 있어야한다. 이때 메서드가 자신이 속한 객체의 프로퍼티를 참조하려면 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.

this 키워드

this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수 self-referencing variable다. this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.

this는 자바스크립트 엔진에 의해 암묵적으로 생성되며, 코드 어디서든 참조할 수 있다. 함수를 호출하면 argument 객체와 this가 암묵적으로 함수 내부에 전달된다. 함수 내부에서 argument 객체를 지역 변수처럼 사용할 수 있는 것처럼 this도 지역 변수처럼 사용할 수 있다.

argument 객체 - MDN

this 바인딩(this가 가리키는 값)은 함수 호출 방식에 의해 동적으로 결정된다.

this 바인딩 bining
바인딩이란 식별자와 값을 연결하는 과정을 의미.
this(키워드로 분류되지만 식별자 역할을 함)와 this가 가리킬 객체를 바인딩함.

// 	객체 리터럴
const circle = {
  radius: 5,
  getDiameter() {
    // this는 메서드를 호출한 객체를 가리킨다.
  	return 2 * this.radius;
  }
};

console.log(circle.getDiameter()); // 10

객체 리터럴의 메서드 내부에서느 this는 메서드를 호출한 객체 circle을 가리킨다.

// 생성자 함수
function Circle(radius) {
  // this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  this.radius = radius;
}
Circle.prototype.getDiameter = function () {
  // this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  return 2 * this.radius;
};

// 인스턴스 생성
const circle = new Circle(5);
console.log(circle.getDiameter()); // 10

자바스크립트의 this는 함수가 호출되는 방식에 따라 this 바인딩이 동적으로 결정된다.

// this는 어디서든지 참조 가능하다.
// 전역에서 this는 전역 객체 window를 가리킨다.
console.log(this);	// window

function square(number) {
  // 일반 함수 내부에서 this는 전역 객체 window를 가리킨다.
  console.log(this); // window
  return number * number;
}
square(2);

const person = {
  name: 'Lee',
  getName() {
    // 메서드 내부에서 this는 메서드를 호출한 객체를 가리킨다.
    console.log(this); // {name: "Lee", getName: ƒ}
    return this.name;
  }	
};
console.log(person.getName()); // Lee

function Person(name) {
  this.name = name;
  // 생성자 함수 내부에서 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  console.log(this;) // Person {name: "Lee"}
}

const me = new Person('Lee');

this는 자기 참조 변수이므로 일반적으로 객체의 메서드 내부 또는 생성자 함수 내부에서만 의미가 있다. 일반 함수 내부에서 this를 사용할 필요가 없기 때문에 strict mode가 적용된 일반 함수 내부의 this에는 undefined가 바인딩된다.

함수 호출 방식과 this 바인딩

렉시컬 스코프와 this 바인딩은 결정시기가 다르다.
함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프 Lexical Scope는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위 스코프를 결정한다. 하지만 this 바인딩은 함수 호출 시점에 결정된다.

  • this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다. 실행 컨텍스트는 함수를 호출할 때 생성되므로 this는 함수를 호출할 때 결정된다는 것.
  • 함수를 어떤 방식으로 호출하느냐에 따라 값이 달라짐.

전역 공간에서의 this

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

Node.js에서의 전역객체는 window 대신 global을 사용한다.

전역변수와 전역객체

var a = 1;
console.log(a);			// 1
console.log(window.a);	// 1
console.log(this.a); 	// 1

전역공간에서의 this는 전역객체를 의미.
자바스크립트의 모든 변수는 특정 객체(실행 컨텍스트의 L.E)의 프로퍼티로서 동작한다.
전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다.
그래서 a를 직접 호출하여도 스코프 체인에서 a를 검색하다가 가장 마지막에 전역 스코프의 L.E, 전역객체에서 해당 프로퍼티 a를 발견해서 그 값을 반환하는 것.
단순히 window.가 생략된 것이라 여겨도 무방함.

삭제 명령시 예외

전역변수 선언과 전역객체의 프로퍼티 할당 사이에 전혀 다른 경우

var a = 1;
delete window.a;					// false
console.log(a, window.a, this.a);	// 1 1 1 
var b = 2;
delete b;							// false
console.log(b, window.b, this.b);	// 2 2 2 
window.c = 3;
delete window.c;					// true
console.log(c, window.c, this.c);	// Uncaught ReferenceError: c is not defined 
window.d = 4;
delete d;							// true
console.log(d, window.d, this.d);	// Uncaught ReferenceError: c is not defined 

처음부터 전역객체의 프로퍼티로 할당한 경우엔 삭제가 되는 반면 전역변수로 선언한 경우에는 삭제가 되지 않는 것을 확인할 수 있음.

전역변수를 선언하면 자바스크립트 엔진이 자동으로 전역객체의 프로퍼티로 할당하면서 추가적으로 해당 프로퍼티의 configurable 속성(변경 및 삭제 가능성)을 false로 정의.

var로 선언한 전역변수와 전역객체의 프로퍼티는 호이스팅 여부 및 configurable 여부에서 차이를 보임.

메서드로서 호출할 때 그 메서드 내부에서의 this

함수 vs. 메서드

함수를 실행하는 가장 일반적인 방법 두 가지.
1. 함수로서 호출
2. 메서드로서 호출

함수는 그 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행한다.

어떤 함수를 객체의 프로퍼티에 할당한다고 해서 그 자체로서 무조건 메서드가 되는 것이 아니라 객체의 메서드로서 호출할 경우에만 메서드로 동작하고, 그렇지 않으면 함수로 동작함.

메서드로서 호출 - 점 표기법, 대괄호 표기법

var obj = {
	method: function (x) { console.log(this, x); }
};
obj.method(1); 		// { method: f } 1
obj['method'](2);	// { method: f } 2

어떤 함수를 호출할 때 그 함수 이름(프로퍼티명) 앞에 객체가 명시되어 있는 경우에는 메서드로 호출한 것이고, 그렇지 않은 모든 경우에는 함수로 호출한 것.

메서드 내부에서의 this

this에는 호출한 주체에 대한 정보가 담긴다. 어떤 함수를 메서드로서 호출하는 경우 호출 주체는 함수명(프로퍼티명) 앞의 객체.

var obj = {
  methodA: function () { console.log(this); },
  inner: {
    methodB: function () { console.log(this); }
  }
};
obj.methodA();			// { methodA: f, inner: {...} } ( === obj )
obj['methodA']();		// { methodA: f, inner: {...} } ( === obj )

obj.inner.methodB();		// { methodB: f }	  ( === obj.inner )
obj.inner['methodB']();		// { methodB: f }	  ( === obj.inner )
obj['inner'].methodB();		// { methodB: f }	  ( === obj.inner )
obj['inner']['methodB']();	// { methodB: f }	  ( === obj.inner )

함수로서 호출할 때 그 함수 내부에서의 this

  • 함수에서의 this는 전역 객체를 가리킨다.
    • 더글라스 크락포드는 명백한 설계상의 오류라고 지적함
  • 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지 등)은 중요하지 않고, 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지 없는지가 관건.

내부함수에서의 this를 우회하는 방법
1. 상위 스코프의 this를 저장해서 내부함수에서 활용하기.
2. (ES6) 화살표 함수 arrow function

외에 call, apply 등의 메서드를 활용하여 함수를 호출할 때 명시적으로 this를 지정하는 방법.

콜백 함수의 경우에도 기본적으로 전역객체를 바라보지만, 제어권을 가지는 함수(메서드)가 콜백 함수에서의 this를 무엇으로 할지를 결정함.

위 사항은 다음 회차에 더욱 공부해볼 예정

생성자 함수 내부에서의 this

생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수.
객체지향 언너에서는 생성자를 클래스 class, 클래스를 통해 만든 객체를 인스턴스 instance라고 함.

프로그래밍적으로 생성자는 구체적인 인스턴스를 만들기 위한 일종의 틀.

자바스크립트는 함수에 생성자로서의 역할을 부여했음. new명령어와 함께 함수를 호출하면 해당 함수가 생성자로서 동작.
어떤 함수가 생성자 함수로서 호출된 경우 내부에서의 this는 곧 새로 만들 구체적인 인스턴스 자신이 됨.

  1. 생성자 함수 호출 - new 명령어와 함께
  2. 생성자의 prototype 프로퍼티를 참조하는 __proto__라는 프로퍼티가 있는 객체(인스턴스) 생성
  3. 미리 준비된 공통 속성 및 개성을 해당 객체(this)에 부여
  4. 구체적인 인스턴스 완성
var Cat = function (name, age) {
  this.bark = '야옹';
  this.name = name;
  this.age = age;
};

var choco = new Cat('초코', 7);
var nabi = new Cat('나비', 5);
console.log(choco, nabi);

/* 결과
Cat { bark: '야옹', name: '초코', age: 7 }
Cat { bark: '야옹', name: '나비', age: 5 }
*/

참조 ✅

  • 📚 『코어 자바스크립트』
    • 3-1. 상황에 따라 달라지는 this
  • 📚 『모던 자바스크립트 Deep Dive』
    • 22.1 this 키워드
    • 22.2.1 ~ 22.2.3 함수 호출 방식과 this 바인딩

0개의 댓글