오늘은 3단원인 "this" 를 포스팅 할 것이다.
지난번 포스팅한 "실행 컨텍스트" 도 읽어보면 도움이 될 듯하다.
https://ilikezzi.tistory.com/55
this....
'이것' 이라는 단어 그 자체로의 뜻을 가지지만, 가끔씩 어디를 가르키는건지 헷갈렸는데
이제 그 헷갈리던 this에 대해서 제대로 잡아보자.
03. this
전역 공간에서의 this
브라우저 환경에서 전역객체는 window
Node.js.환경에서 전역객체는 global
전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다.
간단한 예제 코드를 보자.
// 브라우저 환경 예시
var a = 1;
console.log(a); = 1
console.log(window.a) = 1
console.log(this.a) = 1
이처럼 자바스크립트의 모든 변수는 실은 특정객체의 프로퍼티이다.
하지만 이는 var에만 해당 되는 얘기다.
var는 함수범위여서 현재 실행 컨텍스트의 전역 객체의 프로퍼티로 할당되지만,
let, const는 블록 범위여서 전역 객체의 프로퍼티로 할당되지 않는다.
var myVar = "hello"; // 전역 객체의 프로퍼티로 할당됨
let myLet = "world"; // 전역 객체의 프로퍼티로 할당되지 않음
console.log(window.myVar); // "hello"
console.log(window.myLet); // undefined
그리고 머쓱하지만 처음 알게된 부분이 있다...
처음부터 전역객체의 프로퍼티로 할당한 경우에는 삭제가 되는 반면,
전역 변수로 선언한 경우에는 삭제가 되지않는다.
뭔소린지 코드로 보자.
var a = 1;
delete window.a; // false
console.log(a, window.a, this.a) // 1 1 1
var b = 2;
delete b; // false
console.log(b, window.b, this.b) // 2 2 2
window.c = 3;
delete window.c; // true
console.log(c, window.c, this.c) // Uncaught ReferenceErrorr: c is not defined
window.d = 4;
delete d; // true
console.log(d, window.d, this.d) // Uncaught ReferenceErrorr: d is not defined
위 두개랑 아래 두개의 차이점을 보자.
위 두개는 전역변수로 선언을 해서 삭제가 안되고,
아래 두개는 처음부터 전역변수의 프로퍼티로 할당을 해서 문제 없이 삭제가 되었다.
왜 그럴까?
이는 사용자가 의도치 않게 삭제하는 것을 방지하는 차원에서 마련한 나름의 방어 전략이라고 해석된다.
전역변수로 선언시, JS엔진이 이를 자동으로 전역객체의 프로퍼티로 할당하면서 추가적으로
해당 프로퍼티의 configurable 속성(변경 및 삭제 가능성)을 false로 정의해버린다.
메서드로서 호출할 때의 this
크게 함수로서의 호출과 메서드로서의 호출이 있는데
메서드로서의 호출은 되게 간단하다.
점 표기법 (.) 이든 대괄호 표기법이든, 어떤 함수를 호출할 때
그 함수이름 앞에 객체가 명시되어 있으면 메서드로서의 호출이다.
var obj = {
method = function(x) { console.log(this, x); }
};
obj.method(1); // { method: f } 1
obj['method'](2); // { method: f } 2
함수로서 호출할 때의 this
이 부분도 생각보다 단순하다.
this 바인딩에 관해서는 함수를 실행하는 당시의 주변환경은 중요하지 않고,
오직 해당 함수를 호출하는 구문 앞에서 점 또는 대괄호 표기가 있는지 없는지가 관건이다.
저번 포스팅인 2단원_실행 컨텍스트에서 나왔던
실행 컨텍스트를 활성화할 당시에 this가 지정되지 않은경우 전역객체를 바라본다고 했다.
ES6에서는 이 문제를 보완하고자 화살표 함수를 도입했다.
화살표 함수는 this를 바인딩하지 않고, 선언될 당시의 상위 스코프의 this를 그대로 사용한다.
그래서 특정 객체의 메소드 안에서 비동기 작업을 할 때 this를 유지하고 싶을 때 많이 사용한다.
즉, 화살표 함수 안에서는 this가 아예 없으며, 스코프체인상 가장 가까운 this에 접근하게 된다.
var obj = {
outer: function () {
console.log(this); // (1) { outer:f }
var innerFunc = () => {
console.log(this); // (2) { outer:f }
};
innerFunc();
}
};
obj.outer();
위 코드 처럼 두 콘솔의 this가 같은 곳을 바라보고 있다.
생성자 함수 내부에서의 this
function Dog(name, breed) {
this.name = name; // 여기서 this는 새로 생성되는 객체를 가리킨다.
this.breed = breed;
}
var myDog = new Dog('Buddy', 'Golden Retriever');
console.log(myDog.name); // 'Buddy'
console.log(myDog.breed); // 'Golden Retriever'
생성자 함수는 일반 함수와 달리 new 키워드와 함께 호출되며,
이때 생성자 함수 내의 this는 생성되는 객체를 가리키게 된다.
명시적으로 this를 바인딩하는 방법
상황별로 this에 어떤값이 바인딩되는지를 살펴봤지만 this에 별도의 대상을 바인딩하는 방법도 있다.
call 메서드
Function.prototype.call(thisArg, arg1, arg2, ...);
call 메서드는 함수를 호출하면서 첫 번째 인자로 this 값을 바인딩하고,
나머지 인자들을 그대로 함수의 파라미터로 전달한다.
- thisArg: this로 바인딩할 값
- arg1, arg2, arg3, ...: 함수에 전달될 인자들
apply 메서드
Function.prototype.apply(thisArg[, argsArray]);
apply 메서드는 call과 비슷한데, 인자들을 배열 형태로 전달한다는 차이가 있다.
- thisArg: this로 바인딩할 값
- [argsArray]: 함수에 전달될 인자들의 배열
call과 apply는 모양만 조금 다를 뿐 기능은 같다.
쉽게말해, 배열로 인자를 넘기고 싶으면 apply, 그렇지 않으면 call을 사용하면 된다.
function Person(name, gender) {
this.name = name;
this.gender = gender;
}
function Student(name, gender, school) {
Person.call(this, name, gender);
this.school = school;
}
function Employee(name, gender, company) {
Person.apply(this, [name, gender]);
this.company = company;
}
const by = new Student('Blanc', 'male', '하버드');
const jn = new Employee('개발', 'male', 'JS');
console.log(by);
// {
// name: 'Blanc',
// gender: 'male',
// school: '하버드'
// }
console.log(jn);
// {
// name: '개발',
// gender: 'male',
// company: 'JS'
// }
이렇게 생성자 내부에서 call , apply 로 다른 생성자를 호출해 반복을 줄일 수 있다.
다음으로 콜백함수 내에서의 this에 대해서 살펴보자.
var report = {
sum: 0,
count: 0,
add: function() {
var args = Array.prototype.slice.call(arguments);
args.forEach(function (entry){
this.sum += entry;
++this.count;
}, this);
},
average: function(){
return this.sum / this.count;
}
};
report.add(60, 85, 95);
console.log(report.sum, report.count, report.average()); // 240 3 80
Array.prototype.slice.call(arguments)를 사용해 배열로 변환하는데,
forEach를 사용해서 배열을 순회한다.
forEach의 두 번째 인수로 this를 전달하면, forEach 내의 콜백 함수에서 이 this가 바인딩된다.
report.add(60, 85, 95)를 호출해서 여기서 this는 report를 바라보게 된다.
확실히 포스팅을 하고나니 this에 대해서 제대로 알게된 것 같다.
다음 챕터는 "콜백 함수"이다
'Programming Language > Javascript' 카테고리의 다른 글
[Javascript] 클로저_코어 자바스크립트 (0) | 2023.09.29 |
---|---|
[Javascript] 콜백 함수_코어 자바스크립트 (0) | 2023.09.04 |
[Javascript] console.dir()로 객체의 모든 depth 구조 출력 (0) | 2023.08.15 |
[Javascript] new Set() 배열에서 쉽게 중복값 제거하기 (0) | 2023.08.14 |
[Javascript] 실행 컨텍스트_코어 자바스크립트 (0) | 2023.08.13 |