[js] this, call, apply, bind
this - JavaScript | MDN
JavaScript에서 함수의 this 키워드는 다른 언어와 조금 다르게 동작합니다. 또한 엄격 모드와 비엄격 모드에서도 일부 차이가 있습니다.
developer.mozilla.org
대부분의 경우 this의 값은 함수를 호출한 방법에 의해 결정
즉,함수 선언이 아닌 호출하는 방법에 따라 달라지므로, 함수가 호출될 때 마다 다를 수 있다.
주의사항 : 화살표 함수는 부모 스코프(선언 기준)
ES5는 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드를 도입
ES2015는 스스로의 this 바인딩을 제공하지 않는 화살표 함수를 추가했다(렉시컬 컨텍스트안의 this값을 유지).
예시 1 : this의 값을 한 문맥에서 다른 문맥으로 넘기려면 다음 예시와 같이 call()이나 apply()를 사용
// call 또는 apply의 첫 번째 인자로 객체가 전달될 수 있으며 this가 그 객체에 묶임
var obj = {a: 'Custom'};
// 변수를 선언하고 변수에 프로퍼티로 전역 window를 할당
var a = 'Global';
//단 let a일 경우, undefined
function whatsThis() {
return this.a; // 함수 호출 방식에 따라 값이 달라짐
}
whatsThis(); // this는 'Global'. 함수 내에서 설정되지 않았으므로 global/window 객체로 초기값을 설정한다.
whatsThis.call(obj); // this는 'Custom'. 함수 내에서 obj로 설정한다.
whatsThis.apply(obj); // this는 'Custom'. 함수 내에서 obj로 설정한다.
예시 2
function add(c, d) {
return this.a + this.b + c + d;
}
var o = {a: 1, b: 3};
// 첫 번째 인자는 'this'로 사용할 객체이고,
// 이어지는 인자들은 함수 호출에서 인수로 전달된다.
add.call(o, 5, 7); // 16
// 첫 번째 인자는 'this'로 사용할 객체이고,
// 두 번째 인자는 함수 호출에서 인수로 사용될 멤버들이 위치한 배열이다.
add.apply(o, [10, 20]); // 34
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // [object Number]
bar.call('foo'); // [object String]
bar.call(undefined); // [object global]
Object.prototype.toString() - JavaScript | MDN
The toString() 은 문자열을 반환하는 object의 대표적인 방법이다
developer.mozilla.org
toString() 메서드는 Object에서 비롯된 모든 객체에 상속
var o = new Object();
o.toString(); // returns [object Object]
The toString() 은 문자열을 반환하는 object의 대표적인 방법이다
function Dog(name) {
this.name = name;
}
const dog1 = new Dog('Gabby');
Dog.prototype.toString = function dogToString() {
return `${this.name}`;
};
console.log(dog1.toString());
// expected output: "Gabby"
toString()으로 객체 클래스 검사
var toString = Object.prototype.toString;
toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
// Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]
화살표 함수
화살표 함수에서 this는 자신을 감싼 정적 범위입니다. 전역 코드에서는 전역 객체를 가리킵니다.
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
참고: 화살표 함수를 call(), bind(), apply()를 사용해 호출할 때 this의 값을 정해주더라도 무시합니다.
// 객체로서 메서드 호출
var obj = {func: foo};
console.log(obj.func() === globalObject); // true
// call을 사용한 this 설정 시도
console.log(foo.call(obj) === globalObject); // true
// bind를 사용한 this 설정 시도
foo = foo.bind(obj);
console.log(foo() === globalObject); // true
어떤 방법을 사용하든 foo의 this는 생성 시점의 것으로 설정됩니다(위 예시에서는 global 객체). 다른 함수 내에서 생성된 화살표 함수에도 동일하게 적용됩니다. this는 싸여진 렉시컬 컨텍스트의 것으로 유지됩니다.
// this를 반환하는 메소드 bar를 갖는 obj를 생성합니다.
// 반환된 함수는 화살표 함수로 생성되었으므로,
// this는 감싸진 함수의 this로 영구적으로 묶입니다.
// bar의 값은 호출에서 설정될 수 있으며, 이는 반환된 함수의 값을 설정하는 것입니다.
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
// obj의 메소드로써 bar를 호출하고, this를 obj로 설정
// 반환된 함수로의 참조를 fn에 할당
var fn = obj.bar();
// this 설정 없이 fn을 호출하면,
// 기본값으로 global 객체 또는 엄격 모드에서는 undefined
console.log(fn() === obj); // true
// 호출 없이 obj의 메소드를 참조한다면 주의하세요.
var fn2 = obj.bar;
// 화살표 함수의 this를 bar 메소드 내부에서 호출하면
// fn2의 this를 따르므로 window를 반환할것입니다.
console.log(fn2()() == window); // true
위 예시에서, obj.bar에 할당된 함수(함수bar라고 지칭)는 화살표 함수로 생성된 다른 함수(익명 함수 B)를 반환
결과로써 함수 B가 호출될 때 B의 this는 영구적으로 obj.bar(함수bar)의 this로 설정됩니다.
반환됨 함수(함수 B)가 호출될 때, this는 항상 초기에 설정된 값일 것입니다.
위 코드 예시에서,
함수 B의 this는 함수 A의 this인 obj로 설정되므로,
일반적으로 this를 undefined나 global 객체로 설정하는 방식으로 호출할 때도
(또는 이전 예시에서처럼 global 실행 컨텍스트에서 다른 방법을 사용할 때에도)
obj의 설정은 유지됩니다. (->??? 무슨 소리인지 잘 모르겠음)
객체의 메서드로서
함수를 어떤 객체의 메서드로 호출하면 this의 값은 그 객체를 사용합니다.
다음 예제에서 o.f()를 실행할 때 o 객체가 함수 내부의 this와 연결됩니다.
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // 37