실행 컨텍스트에 대한 정리를 하다 this 바인딩 부분도 공부할 것이 많아 따로 정리하고싶었다.
(1)에서는 this의 정의와 쓰이게 된 이유, 일반 함수 호출, 메서드 호출, 생성자 함수 호출 등 상황별로 어떤 값이 this에 바인딩 되는지를 알아볼 것이다.
(2)에서는 규칙을 깨고 별도의 대상을 명시적으로 this에 바인딩하는 방법에 대해 알아볼 것이다.
객체는 상태 state를 나타내는 프로퍼티와 동작 behavior을 나타내는 메서드를 하나의 논리적인 단위로 묶은 복합적인 자료구조다.
동작을 나타내는 메서드는 자신이 속한 객체의 상태, 프로퍼티를 참조하고 변경할 수 있어야한다. 이때 메서드가 자신이 속한 객체의 프로퍼티를 참조하려면 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.
this
는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수 self-referencing variable다.this
를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.
this
는 자바스크립트 엔진에 의해 암묵적으로 생성되며, 코드 어디서든 참조할 수 있다. 함수를 호출하면 argument
객체와 this
가 암묵적으로 함수 내부에 전달된다. 함수 내부에서 argument
객체를 지역 변수처럼 사용할 수 있는 것처럼 this
도 지역 변수처럼 사용할 수 있다.
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 바인딩은 결정시기가 다르다.
함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프 Lexical Scope는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위 스코프를 결정한다. 하지만 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 여부에서 차이를 보임.
함수를 실행하는 가장 일반적인 방법 두 가지.
1. 함수로서 호출
2. 메서드로서 호출
함수는 그 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행한다.
어떤 함수를 객체의 프로퍼티에 할당한다고 해서 그 자체로서 무조건 메서드가 되는 것이 아니라 객체의 메서드로서 호출할 경우에만 메서드로 동작하고, 그렇지 않으면 함수로 동작함.
메서드로서 호출 - 점 표기법, 대괄호 표기법
var obj = {
method: function (x) { console.log(this, x); }
};
obj.method(1); // { method: f } 1
obj['method'](2); // { method: f } 2
어떤 함수를 호출할 때 그 함수 이름(프로퍼티명) 앞에 객체가 명시되어 있는 경우에는 메서드로 호출한 것이고, 그렇지 않은 모든 경우에는 함수로 호출한 것.
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를 우회하는 방법
1. 상위 스코프의 this를 저장해서 내부함수에서 활용하기.
2. (ES6) 화살표 함수 arrow function
외에 call, apply 등의 메서드를 활용하여 함수를 호출할 때 명시적으로 this를 지정하는 방법.
콜백 함수의 경우에도 기본적으로 전역객체를 바라보지만, 제어권을 가지는 함수(메서드)가 콜백 함수에서의 this
를 무엇으로 할지를 결정함.
위 사항은 다음 회차에 더욱 공부해볼 예정
생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수.
객체지향 언너에서는 생성자를 클래스 class, 클래스를 통해 만든 객체를 인스턴스 instance라고 함.
프로그래밍적으로 생성자는 구체적인 인스턴스를 만들기 위한 일종의 틀.
자바스크립트는 함수에 생성자로서의 역할을 부여했음. new
명령어와 함께 함수를 호출하면 해당 함수가 생성자로서 동작.
어떤 함수가 생성자 함수로서 호출된 경우 내부에서의 this
는 곧 새로 만들 구체적인 인스턴스 자신이 됨.
new
명령어와 함께prototype
프로퍼티를 참조하는 __proto__
라는 프로퍼티가 있는 객체(인스턴스) 생성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 }
*/