728x90
이번에는 JavaScript의 this에 대해 알아보려고 한다. javascript의 this는 다른 언어들과 조금 다르게 동작한다.
Javascript의 this는 대부분의 경우 함수를 호출한 방법에 의해 결정된다. 그렇기 때문에 함수를 호출할 때마다 this가 가리키는 값이 다를 수 있고, 이는 혼동을 줄 수 있다.
이를 위해 ES5는 bind()메서드를 도입하여 함수를 어떻게 호출했는지에 상관없이 this값을 설정할 수 있도록 하였고
ES2015는 스스로의 this바인딩을 제공하지 않는 화살표 함수를 추가하였다. (화살표 함수는 렉시컬 컨텍스트 안의 this값을 유지한다)
이번에는 this의 동작방식의 예시들을 알아보고, 어떻게 사용해야하는지 알아보겠다!
* 참고로 이 게시글의 예시들은 비엄격모드에 대한 예시입니다! 엄격모드는 다를 수 있으니 참고해주세요.
전역 문맥에서의 this
// 웹 브라우저에서는 window 객체가 전역 객체
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"
- 전역 실행 맥락에서의 this는 전역 객체인 window를 참조한다.
- globalThis 프로퍼티를 이용한다면 현재 컨텍스트와 관계없이 항상 전역 객체를 얻을 수도 있다.
함수 문맥에서의 this
- 함수 내부에서의 this 값은 함수를 호출한 방법에 의해 좌우된다
- 함수 내부의 this는 지정되지 않는다. 지정되지 않으면 기본 값으로 전역 객체를 참조하게 됨
function f1() {
return this;
}
// 브라우저
f1() === window; // true
// Node.js
f1() === global; // true
- 여기서 this의 값은 호출에 의해 설정되지 않았기 때문에 기본값인 전역객체(window)를 참조한다
call()과 apply()를 사용하여 명시적으로 객체 지정
// call 또는 apply의 첫 번째 인자로 객체가 전달될 수 있으며 this가 그 객체에 묶임
var obj = { a: "Custom" };
// 변수를 선언하고 변수에 프로퍼티로 전역 window를 할당
var a = "Global";
function whatsThis() {
return this.a; // 함수 호출 방식에 따라 값이 달라짐
}
whatsThis(); // this는 'Global'. 함수 내에서 설정되지 않았으므로 global/window 객체로 초기값을 설정한다.
whatsThis.call(obj); // this는 'Custom'. 함수 내에서 obj로 설정한다.
whatsThis.apply(obj); // this는 'Custom'. 함수 내에서 obj로 설정한다.
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
- call과 apply를 사용하여 this로 사용할 객체를 직접 넣어줄 수도 있다.
- 명시적으로 this에 사용할 객체를 넣어준다고 생각하면 된다
- 이러면 누가 호출하든 상관없이 매개변수에 해당하는 객체가 this의 주체가 된다.
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // [object Number]
bar.call("foo"); // [object String]
bar.call(undefined); // [object global]
- call과 apply는 this로 전달된 값이 객체가 아닌 경우 call과 apply는 이를 객체로 변환하기 위한 시도를 한다
- null과 undefined는 전역객체로 변환되고,
- 7, 'foo'와 같은 원시값은 관련 생성자를 사용해 객체로 변환한다(Number, String)
bind메서드
function f() {
return this.a;
}
var g = f.bind({ a: "azerty" });
console.log(g()); // azerty
var h = g.bind({ a: "yoo" }); // bind는 한 번만 동작함!
console.log(h()); // azerty
var o = { a: 37, f: f, g: g, h: h };
console.log(o.a, o.f(), o.g(), o.h()); // 37, 37, azerty, azerty
- bind에서는 다음과 같이 첫 번째 호출된 객체를 this로 지정해주는 함수이다.
- 호출 방식에 상관없이 영구적으로 bind()의 첫 번째 매개변수로 고정된다
화살표 함수에서의 this ★★★
var globalObject = this;
var foo = () => this;
console.log(foo() === globalObject); // true
// 객체로서 메서드 호출
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
- 화살표함수는 함수를 call(), apply(), bind()를 사용하여 this를 지정해줘도 무시한다.
- 어떤 방법을 써도 생성 시점의 this로 고정이된다
- 함수를 호출한 상위 환경의 this다
- 화살표 함수는 자신만의 this를 갖지 않고 자신을 호출한 상위의 this로 고정된다고 생각하면 되겠다.
var obj = {
bar: function () {
var x = () => this;
return x;
},
};
var fn = obj.bar();
// this 설정 없이 fn을 호출하면 기본값으로 global 객체
console.log(fn() === obj); // true
var fn2 = obj.bar;
// 화살표 함수의 this를 bar 메소드 내부에서 호출하면 fn2의 this를 따르므로 window
console.log(fn2()() == window); // true
- bar 메서드는 화살표 함수로 생성되었으므로, this는 감싸진 함수의 this로 영구적으로 묶인다
- this가 포함된 화살표함수를 fn에 받으면 해당 this는 자신의 호출한 obj로 고정됨
- 하지만 bar를 받고 this가 포함된 화살표혐수를 직접 호출해주면 해당 this는 window가 된다
- 화살표함수가 정의된 곳 바깥 스코프의 문맥에 의해 this가 정의된다고 생각하면 편하다.
const cat = {
name: 'meow',
foo1: function() {
const foo2 = function() {
console.log(this.name);
}
foo2();
}
};
cat.foo1(); // undefined
const cat2 = {
name: 'meow',
foo1: function() {
const foo2 = () => {
console.log(this.name);
}
foo2();
}
};
cat2.foo1(); // meow
- 첫 번째 예시처럼 함수가 중첩된 경우, undefined가 출력된다
- 이는 함수를 호출 시 함수 내부에서의 this가 지정되지 않기 때문이다
- 객체안의 메소드일 경우라도 그 안에 내부함수의 this는 전역객체를 가리킴
- (이는 js개발자 피셜 설계상의 오류라고 한다 - 더글라스 크락포드의 자바스크립트 핵심 가이드 중 언급)
- 하지만 화살표 함수로 정의할 경우, 정상적으로 값이 나온다.
객체의 메서드★★★
var o = {
prop: 37,
f: function () {
return this.prop;
},
};
console.log(o.f()); // 37
- 함수를 어떤 객체의 메서드로 호출하면 this값은 그 객체를 사용한다
- 함수가 정의된 방법이나 위치에 전혀 영향을 받지 않는다!!! 중요
- 이 this바인딩은 가장 즉각으로 멤버의 영향을 받는다
var o = { prop: 37 };
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37
prop = 40;
o.b = { g: independent, prop: 42 };
console.log(o.b.g()); // logs 42
- 해당 객체를 거쳐 함수를 호출 -> 해당 this는 그 함수다!!
- o.b의 경우, o.b가 g의 호출하는 객체이기 때문에 this는 o.b가 된다
- 가장 즉각적인 참조가 o.b이기 때문
this가 지정되지 않은 경우
자동으로 전역 객체를 바라본다
함수 호출 시 함수
+)
- 객체 프로토타입 체인에서의 this는 해당 객체의 값을 this로 취한다
- 메서드가 어떤 객체의 프로토타입 체인 위에 존재해도 현재 객체가 this이다.
- java에서 자식에는 없는 부모의 메서드를 호출했을 때, this가 가리키는 게 자식이라고 생각하면 되겠다. (다형성 느낌)
- 함수를 접근자와 설정자에서 호출해도 this는 접근하거나 설정하는 속성을 가진 객체로 묶인다.
- 생성자로 사용 시 this는 새로 생긴 객체에 묶인다
- 함수를 이벤트 처리기로 사용 시 this는 이벤트를 발사한 요소이다 (일부 브라우저는 addEventListener()외에 다른 방법으로 추가한 처리기에 대해선 이 규칙 따르지 않음)
- 코드를 인라인 이벤트 처리기로 사용 시 this는 처리기를 배치한 DOM요소
참고자료
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this#as_an_object_method
728x90
'Frontend > JavaScript' 카테고리의 다른 글
[JS] setTimeout()과 setInterval() 함수를 알아보자! (1) | 2024.03.07 |
---|