JavaScript에서 this
는 함수가 어떻게 호출되었는지에 따라 this
에 바인딩할 객체가 동적으로 결정됩니다. 그리고 함수가 호출되는 방식은 4가지로 나눌 수 있습니다.
각종 값들이 확정되어 더 이상 변경할 수 없는 구속(Bind) 상태가 되는 것.
전역 객체는 모든 객체의 유일한 최상위 객체를 의미하며, 브라우저 환경에서는 window
, Node.js 환경에서는 global
객체를 의미합니다. 이는 전역 객체 호출 시 환경에 맞춰 호출해야 한다는 것을 의미하기도 합니다. 더 자세한 내용은 이 링크에서 확인해주세요.
// 먼저, 모두 비엄격모드(=non-strict mode) 가정
// 브라우저 환경
this === window // true
this === global // ReferenceError: global is not defined
// Node.js 환경
node
this === global // true
this === window // ReferenceError: window is not defined
this
C The Strict Mode of ECMAScript
If this is evaluated within strict mode code , then the this value is not coerced to an object. A this value of undefined or null is not converted to the global object and primitive values are not converted to wrapper objects. The this value passed via a function call (including calls made using Function.prototype.apply and Function.prototype.call) do not coerce the passed this value to an object ( 9.2.1.2 , 19.2.3.1 , 19.2.3.3 ).
http://www.ecma-international.org/ecma-262/#sec-strict-mode-of-ecmascript
위는 ECMAScript에 명시된 엄격 모드에서의 this
에 관한 설명입니다. 여기서 몇 가지를 알 수 있습니다.
this
값이 undefined
혹은 null
이라면, 비엄격모드일 경우 this
에 전역 객체를 바인딩합니다. 하지만, 엄격 모드라면 바인딩하지 않습니다.this
값이 원시값(primitive value)이라면, 비엄격모드일 경우 autoboxing이 수행됩니다. 하지만, 엄격 모드라면 수행되지 않습니다(=원시 값 그대로를 가집니다). this
값이 Function.prototype.call 혹은 Function.prototype.apply에 의해 전달된 경우 또한 동일합니다.위에 대한 예시입니다. 즉, 본래 주어진 this
의 값을 그대로 유지하기 위해 엄격모드를 사용합니다.
// 브라우저 환경, 비엄격모드(=non-strict mode) 가정
function nonStrictFunc() {
return this;
}
function strictFunc() {
'use strict'; // 함수 단위의 엄격모드 설정
return this;
}
// this 값이 undefined인 경우
console.log(nonStrictFunc() === window); // true
console.log(strictFunc() === window); // false
console.log(strictFunc()); // undefined
// this 값이 원시값인 경우
console.log(nonStrictFunc.bind('123')() === '123'); // false
console.log(strictFunc.bind('123')() === '123'); // true
// Function.prototype.call인 경우
console.log(nonStrictFunc.call('123') === '123'); // false
console.log(strictFunc.call('123') === '123'); // true
this
를 결정하는 4가지 상황다시 돌아왔습니다. 이제 this
가 결정되는 4가지 상황에 대해 설명하겠습니다.
기본적으로 일반적인 함수 호출 시, this
는 전역 객체(Global Object)에 바인딩됩니다. 이는 나머지 함수 호출 방식들을 제외하면 this
는 전역객체를 나타낸다는 것을 의미합니다. 예를 들어, (1) 전역 함수, (2) 내부 함수, (3) 객체 메서드의 내부 함수, (4) 콜백 함수 역시 전역 객체입니다.
// 브라우저 환경, 비엄격모드(=non-strict mode) 가정
function foo() { // 👈 (1) 전역함수
console.log("foo’s this:", this); // window
bar();
function bar() { // 👈 (2) 내부함수
console.log("bar’s this:", this); // window
}
}
foo();
var obj = {
foo: function() {
console.log("foo’s this:", this); // obj
bar();
function bar() { // 👈 (3) 객체 메소드의 내부함수
console.log("bar’s this:", this); // window
}
}
};
obj.foo();
setTimeout(function() { // 👈 (4) 콜백함수
console.log("callback’s this:", this); // window
}, 1000);
JavaScript methods are actions that can be performed on objects.
A JavaScript method is a property containing a function definition.
객체에서 메서드는 함수 정의를 포함한 속성을 뜻합니다. 즉, 객체의 속성 중 함수인 것이 메서드입니다. this
는 메서드에서 호출될 때, 해당 메서드를 호출한 객체에 바인딩됩니다. 이 규칙은 프로토타입 객체(Prototype Object)에도 동일하게 적용됩니다.
// 브라우저 환경, 비엄격모드(=non-strict mode) 가정
var obj1 = {
name: 'Lee',
sayName: function() {
console.log(this.name);
}
};
var obj2 = {
name: 'Kim'
};
obj2.sayName = obj1.sayName; // 객체 obj2에 새로운 속성 추가
obj1.sayName(); // Lee
obj2.sayName(); // Kim
var sayName = obj1.sayName;
sayName(); // undefined (=window.name)
생성자(constructor) 함수는 객체 생성하는 역할을 합니다. 하지만, 자바스크립트에서 생성자 함수는 별도로 형식이 정해져 있지 않습니다. 왜냐하면 모든 함수는 선언 시 생성자 함수 자격을 가지며, new
연산자를 붙여서 호출하면 해당 함수가 생성자 함수로 동작하기 때문입니다. 그러나 구분의 편의성을 위해 생성자 함수는 관습적으로 첫 문자를 대문자로 명시합니다.
function Person(name) {
this.name = name;
}
var someone = new Person('Lee');
console.log(someone.name) // Lee
이때 생성자 함수에서 this
는 new
연산자에 의해 새롭게 만들어진 객체에 바인딩됩니다. 즉, this
는 새로 생성된 객체를 나타냅니다.
this
에 바인딩될 객체는 함수 호출 방식에 따라 결정되지만, 사용자가 명시적으로 this
에 바인딩될 객체를 지정할 수 있는 방법이 있습니다. 바로 Function.prototype.apply
, Function.prototype.call
, Function.prototype.bind
를 사용하는 것입니다. 각 메서드의 활용 예제는 아래와 같습니다.
// Function.apply();
var numbers = [5, 6, 2, 3, 7];
var max = Math.max.apply(null, numbers);
console.log(max); // 7
// Function.call()
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
console.log(new Food('cheese', 5).name); // "cheese"
// Function.bind()
var module = {
x: 42,
getX: function() {
return this.x;
}
}
var unboundGetX = module.getX;
var boundGetX = unboundGetX.bind(module);
console.log(unboundGetX()); // undefined
console.log(boundGetX()); // 42
this
는 본래 값을 유지하나, 비 엄격 모드에선 값에 따라 전환됩니다.this
는 해당 메서드를 소유하고 있는 객체를 나타냅니다.this
는 new
연산자를 통해 새로 생성될 객체를 나타냅니다.this
에 바인딩할 값을 변경할 수도 있습니다.this
는 전역 객체를 나타냅니다.하지만 아직 스스로 생각하기에도 글이 정리가 되지 않은 느낌이 듭니다. 문제가 되는 부분은 언제든 지적 부탁드립니다.