엄격모드와 비엄격 모드에서 일부 차이가 있다는 점을 알려드리고 싶습니다.
엄격모드(strict mode)?
자바스크립트는 꽤 오랫동안 호환성 이슈 없이 발전해왔습니다. 기존의 기능을 변경하지 않으면서 새로운 기능이 추가되었죠.
덕분에 기존에 작성한 코드는 절대 망가지지 않는다는 장점이 있었습니다. 하지만 자바스크립트 창시자들이 했던 실수나 불완전한 결정이 언어 안에 영원히 박제된다는 단점도 생겼습니다.
이런 상황은 ECMAScript5(ES5)가 등장하기 전인 2009년까지 지속되었습니다. 그런데 새롭게 제정된 ES5에서는 새로운 기능이 추가되고 기존 기능 중 일부가 변경되었습니다. 기존 기능을 변경하였기 때문에 하위 호환성 문제가 생길 수 있겠죠? 그래서 변경사항 대부분은 ES5의 기본 모드에선 활성화되지 않도록 설계되었습니다. 대신 use strict라는 특별한 지시자를 사용해 엄격 모드(strict mode)를 활성화 했을 때만 이 변경사항이 활성화되게 해놓았습니다.
출처(https://ko.javascript.info/strict-mode)
여기서는 엄격모드는 안따를예정.
기본적으로는 상위 객체 그 자체를 가져다 쓴다.
대부분의 경우, this
값은 함수를 호출한 방법에 의해 결정된다(실행 중에 할당으로 설정할수 없다).
다음의 예제를 보자.
const test = {
prop: 42,
func: function () {
return this.prop;
},
};
console.log(test.func()); //output: 42
기본적으로 this
는 상위 객체 그 자체를 받는다. func
의 상위객체는 test
이다. 따라서 this===test
인 것이다.
전역에서 this
를 호출하면 어케되나?
전역에는 (당연하게도) 상위객체가 없다. 전역 자체가 최상위객체니까...(전역도 하나의 객체다. 웹브라우저에서는 window 객체가 전역 객체이다.)
그래서 전역에서의 this
는 전역객체 그 자체를 받는다.
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"
참고로 globalThis
라는 속성도 있다. 함수 안에서 전역객체를 가진 this 값을 가진다.
앞서 말했듯이 this
값은 상위 객체 그 자체를 값으로 가진다.
만약 내가 함수라면 누가 나를 호출했는지가 중요할 것이다. ✨나를 호출한 그 녀석이 나의 상위 객체이기 때문이다.✨
다음의 예제를 보자.
functions f1() {
return this;
}
console.log(f1() === window); //true
f1
이라는 함수에서 this
를 호출하고 있다. 그렇다면 this
값은 f1
의 상위객체일 것이다.
f1
의 상위객체는..? 전역객체이다.
따라서 this
는 window
(전역객체)가 된다.
call()
과 apply()
로 나의 함수 문맥을 다른 함수 문맥에게 전해주기예시 1
// 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로 설정한다.
call()
과 apply()
는 첫번째 인자로 전달된 객체를 this
로 묶을 수 있다.(this
값이 그 객체가 된다는 의미이다.)
또한 call()
과 apply()
는 변수를 전달하는 방식이 다르다.
✔ call()
은 변수의 값을 풀어서 전달한다.
✔ apply()
는 변수의 값을 하나의 배열로 묶어서 전달한다.
그래서 call()
을 쓰느냐 apply()
를 쓰느냐에 따라 결과가 달라질 수 있다.
다음의 예제를 보자.
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
apply()
를 쓰면 배열이 들어있는 변수를 그대로 인자로 넘겨줄 수 있기 때문에 유용하게 사용할 수 있다.
또한 객체가 아닌 값을 넘겨주는 경우 그 값을 객체로 변환하려고 한다.
null
이나undefined
는 전역 객체,7
은 Number 객체,'어쩌구'
는 String 객체가 되는 식이다.
bind()
메서드로 this 고정하기bind()
메서드로 전달되는 첫번째 매개변수가 this
로 고정된다.
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
화살표 함수에서 this
는 자신이 포함된 정적 범위이다.
다시 말해, 내가 선언된 시점에서의 범위이다.
(일반적인 함수의 경우에는 내가 호출된 시점에서의 범위이다. 이 차이점에 유의하자.)
따라서 call()
, apply()
또는 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
객체나 함수 안에서 선언된 화살표 함수를 불러올 땐 주의해야 한다.
// 화살표 함수의 this는 선언된 이 시점에서, 자신이 포함된 상위 객체로 고정된다.
// 따라서 function() {...}이 어떻게 호출되느냐에 따라 달라진다.
var obj = {
bar: function () {
var x = () => this;
return x;
},
};
// **obj의 메소드로써** bar를 호출하면 this가 obj로 설정된다
// 반환된 화살표함수 그 자체를 fn에 할당
var fn = obj.bar();
// obj의 메서드 bar에서 생성된 fn이기 때문에
// fn의 상위객체는 obj가 된다
console.log(fn() === obj); // true
// 호출 없이 obj의 메소드를 참조한다면 주의하세요.
var fn2 = obj.bar;
// 화살표 함수의 this를 bar 메소드 내부에서 호출하면
// fn2의 this를 따르므로 window를 반환할것입니다.
console.log(fn2()() == window); // true