var ga = 'Global variable';
console.log(this.ga); // === window.ga
function a() { console.log(this); };
a(); // window
this는 기본적으로 window 이다.
일반 함수 내에서 혼자 this를 선언하면, 그 this는 window객체를 가르킨다.
var obj = {
a: function() { console.log(this); },
};
obj.a(); // obj
객체 메서드 a 안의 this는 객체 obj를 가리킨다.
객체의 메서드를 호출할 때 this를 내부적으로 바꿔주기 때문이다.
var a2 = obj.a;
a2(); // window
❗ a2는 obj.a를 꺼내온 것이기 때문에 더 이상 obj의 메서드가 아니고 변수에 담긴 그냥 일반함수 이다.
호출하는 함수가 객체의 메서드인지 그냥 함수인지가 중요하다.
함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니고, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.
함수의 호출하는 방식은 아래와 같이 다양하다.
- 함수 호출
- 메소드 호출
- 생성자 함수 호출
- 콜백 호출
- apply/call/bind 호출
function foo() {
console.log("foo's this: ", this); // window
function bar() {
console.log("bar's this: ", this); // window
}
bar();
}
foo();
일반 전역함수는 물론이고 내부함수의 경우도 this는 외부함수가 아닌 전역객체에 바인딩된다.
var value = 1;
var obj = {
value: 100,
foo: function() {
console.log("foo's this: ", this); // obj
console.log("foo's this.value: ", this.value); // 100
function bar() { /* 내부함수 */
console.log("bar's this: ", this); // window
console.log("bar's this.value: ", this.value); // 1
}
bar();
}
};
obj.foo();
객체의 메소드의 내부함수일 경우에도 this는 전역객체에 바인딩된다.
// example 1
var value = 1;
var obj = {
value: 100,
foo: function() {
setTimeout(function() { /* 콜백함수 */
console.log("callback's this: ", this); // window
console.log("callback's this.value: ", this.value); // 1
}, 100);
}
};
obj.foo();
콜백함수의 경우에도 this는 전역객체에 바인딩된다.
// example 2
let userData = {
signUp: '2020-10-06 15:00:00',
id: 'minidoo',
name: 'Not Set',
setName: function(firstName, lastName) {
this.name = firstName + ' ' + lastName;
}
}
function getUserName(firstName, lastName, callback) {
callback(firstName, lastName);
}
getUserName('PARK', 'MINIDDO', userData.setName);
console.log('1: ', userData.name); // Not Set
console.log('2: ', window.name); // PARK MINIDDO
첫 번째 콘솔의 값이 PAKR MINIDDO 이기를 기대했지만, Not Set이 출력된다.
setName() 함수가 실행되기 전의 name 값이 나오는 것인데, 이는 getUserName() 이 전역 함수이기 때문이다.
userData.setName를 아규먼트로 넘겨줄때 CALL BY VALUE로 가는걸 명심해야 한다. (JS는 무조건 call by value)
한마디로 함수가 복사되어 callback 파라미터에 담기게 되니, 당연히 setName()의 this는 전역객체 window를 가리키게 되는 것이다.
내부함수는 일반 함수, 메소드, 콜백함수 어디에서 선언되었든 관계없이 this는 전역객체를 바인딩한다.
var value = 1;
var obj = {
value: 100,
foo: function() {
var that = this; // Workaround : this === obj
console.log("foo's this: ", this); // obj
console.log("foo's this.value: ", this.value); // 100
function bar() {
console.log("bar's this: ", this); // window
console.log("bar's this.value: ", this.value); // 1
console.log("bar's that: ", that); // obj
console.log("bar's that.value: ", that.value); // 100
}
bar();
}
};
obj.foo();

var obj1 = {
name: 'Lee',
sayName: function() {
console.log(this.name);
}
}
var obj2 = {
name: 'Kim'
}
obj2.sayName = obj1.sayName;
obj1.sayName(); // Lee
obj2.sayName(); // Kim

함수가 객체의 프로퍼티 값이면 메소드로서 호출된다.
이때 메소드 내부의 this는 해당 메소드를 소유한 객체, 즉 해당 메소드를 호출한 객체에 바인딩된다.
function Person(name) {
this.name = name;
}
Person.prototype.getName = function() {
return this.name;
}
var me = new Person('Lee');
console.log(me.getName());
// Lee
// 우선 프로토타입에서 name프로퍼티를 찾는다. 없으니 체이닝에 의해 me 객체에서 찾아서 반환
Person.prototype.name = 'Kim';
console.log(Person.prototype.getName());
// Kim
// 우선 프로토타입에서 name프로퍼티를 찾는다. 찾았으니 반환.

프로토타입 객체도 메소드를 가질 수 있다.
프로토타입 객체 메소드 내부에서 사용된 this도 일반 메소드 방식과 마찬가지로 해당 메소드를 호출한 프로토타입 오브젝트 객체에 바인딩된다.
그래서 this의 프로퍼티를 찾을 때 우선, 직접 바인딩 되어잇는 프로토타입 오브젝트에서 찾고, 없으면 체이닝에 의해 new생성자로 생성된 객체에서 찾게 된다.
// 생성자 함수
function Person(name) {
this.name = name;
}
var me = new Person('Lee');
console.log(me); // Person {name: "Lee"}
// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person('Kim');
console.log(you); // undefined
자바스크립트의 생성자 함수는 말 그대로 객체를 생성하는 역할을 한다.
하지만 자바와 같은 객체지향 언어의 생성자 함수와는 다르게 그 형식이 정해져 있는 것이 아니라
기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.
이는 반대로 생각하면 생성자 함수가 아닌 일반 함수에 new 연산자를 붙여 호출하면 생성자 함수처럼 동작할 수 있다.
따라서 일반적으로 생성자 함수명은 첫문자를 대문자로 기술하여 혼란을 방지하려는 노력을 한다.
var func = function (a, b, c) {
console.log(this, a, b, c);
};
// no binding
func(1, 2, 3); // Window{ ... } 1 2 3
// 명시적 binding
// func 안에 this에는 {x: 1}이 binding 된다.
func.call({ x: 1 }, 4, 5, 6}; // { x: 1 } 4 5 6
var func = function (a, b, c) {
console.log(this, a, b, c);
};
func.apply({ x: 1 }, [4, 5, 6]); // { x: 1 } 4 5 6
var obj = {
a: 1,
method: function (x, y) {
console.log(this.a, x, y);
}
};
obj.method.apply({ a: 4 }, [5, 6]); // 4 5 6
var func = function (a, b, c, d) {
console.log(this, a, b, c, d);
};
func(1, 2, 3, 4); // window객체
// 함수에 this 미리 적용
var bindFunc1 = func.bind({ x: 1 }); // 바로 호출되지는 않아요! 그 외에는 같아요.
bindFunc1(5, 6, 7, 8); // { x: 1 } 5 6 7 8
// 부분 적용 함수 구현
var bindFunc2 = func.bind({ x: 1 }, 4, 5); // 4와 5를 미리 적용
bindFunc2(6, 7); // { x: 1 } 4 5 6 7
bindFunc2(8, 9); // { x: 1 } 4 5 8 9
var func = function (a, b, c, d) {
console.log(this, a, b, c, d);
};
var bindFunc = func.bind({ x:1 }, 4, 5);
// func와 bindFunc의 name 프로퍼티의 차이를 살펴보세요!
console.log(func.name); // func
console.log(bindFunc.name); // bound func
var obj = {
outer: function() {
console.log(this);
var innerFunc = function () {
console.log(this);
}.bind(this); // innerFunc에 this를 결합한 새로운 함수를 할당
innerFunc();
}
};
obj.outer();
var obj = {
logThis: function () {
console.log(this);
},
logThisLater1: function () {
// 0.5초를 기다렸다가 출력해요. 정상동작하지 않아요.
// 콜백함수도 함수이기 때문에 this를 bind해주지 않아서 잃어버렸어요!(유실)
setTimeout(this.logThis, 500);
},
logThisLater2: function () {
// 1초를 기다렸다가 출력해요. 정상동작해요.
// 콜백함수에 this를 bind 해주었기 때문이죠.
setTimeout(this.logThis.bind(this), 1000);
}
};
obj.logThisLater1();
obj.logThisLater2();
var obj = {
outer: function () {
console.log(this); // obj
var innerFunc = () => {
console.log(this); // obj
};
innerFunc();
};
};
obj.outer();