1. 상황에 따라 달라지는 this
1. this는 실행 컨텍스트가 생성될 때 결정된다. 즉, this를 bind한다라고도 한다. this는 함수를 호출할 때 결정된다.
- 전역 공간에서의 this
1. 전역 공간에서 this는 전역 객체를 가르킨다.
2. 런타임 환경에 따라 this는 window(브라우저 환경) 또는 global(node 환경)을 각각 가르킨다.
2. 메서드로서 호출할 때 그 메서드 내부에서의 this
- 함수 vs. 메서드 (독립성)
==> 함수는 그 자체로 독립적인 기능을 수행
==> 메서드는 자신을 호출한 대상 객체에 대한 동작을 수행 - this의 할당
// CASE1 : 함수
// 호출 주체를 명시할 수 없기 때문에 this는 전역 객체를 의미해요.
var func = function (x) {
console.log(this, x);
};
func(1); // Window { ... } 1
// CASE2 : 메서드
// 호출 주체를 명시할 수 있기 때문에 this는 해당 객체(obj)를 의미해요.
// obj는 곧 { method: f }를 의미하죠?
var obj = {
method: func,
};
obj.method(2); // { method: f } 2
- 함수로서의 호출과 메서드로서의 호출 구분 기준 : . []
var obj = {
method: function (x) { console.log(this, x) }
};
obj.method(1); // { method: f } 1
obj['method'](2); // { method: f } 2
- 메서드 내부에서의 this
var obj = {
methodA: function () { console.log(this) },
inner: {
methodB: function() { console.log(this) },
}
};
obj.methodA(); // this === obj
obj['methodA'](); // this === obj
obj.inner.methodB(); // this === obj.inner
obj.inner['methodB'](); // this === obj.inner
obj['inner'].methodB(); // this === obj.inner
obj['inner']['methodB'](); // this === obj.inner
3. 함수로서 호출할 때 그 함수 내부에서의 this
- 함수 내부에서의 this
- 어떤 함수를 함수로서 호출할 경우, this는 지정되지 않는다.
- 실행컨텍스트를 활성화할 당시 this가 지정되지 않은 경우, this는 전역 객체를 의미
- 함수로서 '독립적으로' 호출할 때는 this는 항상 전역객체를 가르킨다.
- 메서드의 내부함수에서의 this
- 메서드의 내부라고 해도, 함수로서 호출한다면 this는 전역 객체를 의미
- this 바인딩에 관해서는 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지)은 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지가 관건이다.
- a메서드의 내부 함수에서의 this 우회
- 변수를 활용하는 방법
==> 내부 스코프에 이미 존재하는 this를 별도의 변수에 할당하는 방법 - 화살표 함수 (=this를 바인딩하지 않는 함수)
- ES6에서 처음 도입. 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 없다.
- 일반 함수와 화살표 함수의 가장 큰 차이점은 <this binding 여부>
- 변수를 활용하는 방법
// 1. 변수를 활용하는 방법
var obj1 = {
outer: function() {
console.log(this); // (1) outer
// AS-IS
var innerFunc1 = function() {
console.log(this); // (2) 전역객체
}
innerFunc1();
// TO-BE
var self = this;
var innerFunc2 = function() {
console.log(self); // (3) outer
};
innerFunc2();
}
};
// 메서드 호출 부분
obj1.outer();
// 2. 화살표 함수
var obj = {
outer: function() {
console.log(this); // (1) obj
var innerFunc = () => {
console.log(this); // (2) obj
};
innerFunc();
}
}
obj.outer();
4. 콜백 함수 호출 시 그 함수 내부에서의 this
// 별도 지정 없음 : 전역객체
setTimeout(function () { console.log(this) }, 300);
// 별도 지정 없음 : 전역객체
[1, 2, 3, 4, 5].forEach(function(x) {
console.log(this, x);
});
// addListener 안에서의 this는 항상 호출한 주체의 element를 return하도록 설계되었음
// 따라서 this는 button을 의미함
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function(e) {
console.log(this, e);
});
- setTimeout 함수, forEach 메서드는 콜백 함수를 호출할 때 대상이 될 this를 지정하지 않으므로, this는 곧 window 객체
- addEventListner 메서드는 콜백 함수 호출 시, 자신의 this를 상속하므로, this는 addEventListner의 앞부분
5. 생성자 함수 내부에서의 this
- 생성자 : 구체적인 인스턴스를 만들기 위한 일종의 틀
- 공통 속성들이 이미 준비돼 있다.
var Cat = function (name, age) {
this.bark = '야옹';
this.name = name;
this.age = age;
};
var choco = new Cat('초코', 7); //this : choco
var nabi = new Cat('나비', 5); //this : nabi
2. 명시적 this 바인딩
==> 자동으로 부여되는 상황별 this의 규칙을 깨고 this에 별도의 값을 저장하는 방법
==> 크게, call / apply / bind로 나눠진다.
- call 메서드
- 호출 주체인 함수를 즉시 실행하는 명령어
- call명령어를 사용하여, 첫 번째 매개변수에 this로 binding할 객체를 넣어주면 명시적으로 binding할 수 있다.
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
// 예시 2
var obj = {
a: 1,
method: function (x, y) {
console.log(this.a, x, y);
}
};
obj.method(2, 3); // 1 2 3
obj.method.call({ a: 4 }, 5, 6); // 4 5 6
- apply 메서드
- call 메서드와 동일. But, this에 binding할 객체는 똑같이 넣어주고 나머지 부분만 배열 형태로 넘겨준다.
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
- call / apply 메서드 활용
1. 유사배열객체(array-like-object)에 배열 메서드를 적용
==> 반드시 length가 필요하다. index번호가 0번부터 시작해서 1씩증가해야한다.
// slice() 함수
//객체에는 배열 메서드를 직접 적용할 수 없어요.
//유사배열객체에는 call 또는 apply 메서드를 이용해 배열 메서드를 차용할 수 있어요.
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
Array.prototype.push.call(obj, 'd');
console.log(obj); // { 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }
var arr = Array.prototype.slice.call(obj);
console.log(arr); // [ 'a', 'b', 'c', 'd' ]
2. Array.from 메서드(ES6)
// 유사배열
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
// 객체 -> 배열
var arr = Array.from(obj);
// 찍어보면 배열이 출력됩니다.
console.log(arr);
3. 생성자 내부에서 다른 생성자를 호출(공통된 내용의 반복 제거)
function Person(name, gender) {
this.name = name;
this.gender = gender;
}
function Student(name, gender, school) {
Person.call(this, name, gender); // 여기서 this는 student 인스턴스!
this.school = school;
}
function Employee(name, gender, company) {
Person.apply(this, [name, gender]); // 여기서 this는 employee 인스턴스!
this.company = company;
}
var kd = new Student('길동', 'male', '서울대');
var ks = new Employee('길순', 'female', '삼성');
4. 여러 인수를 묶어 하나의 배열로 전달할 때 apply 사용
//비효율
var numbers = [10, 20, 3, 16, 45];
var max = min = numbers[0];
numbers.forEach(function(number) {
// 현재 돌아가는 숫자가 max값 보다 큰 경우
if (number > max) {
// max 값을 교체
max = number;
}
// 현재 돌아가는 숫자가 min값 보다 작은 경우
if (number < min) {
// min 값을 교체
min = number;
}
});
console.log(max, min);
//효율
var numbers = [10, 20, 3, 16, 45];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max, min);
// 펼치기 연산자(Spread Operation)를 통하면 더 간편하게 해결도 가능해요
const numbers = [10, 20, 3, 16, 45];
const max = Math.max(...numbers);
const min = Math.min(...numbers);
console.log(max min);
- bind 메서드
- call과 비슷해 보인다. But, call과는 다르게 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하는 메서드
- 목적
- 함수에 this를 미리 적용
- 부분 적용 함수 구현할 때 용이
1. 예시
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
2. name 프로퍼티
==> bind 메서드를 적용해서 새로 만든 함수는 name 프로퍼티에 'bound'라는 접두어가 붙는다.
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
3. 상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기
- 내부함수
1. 메서드의 내부함수에서 메서드의 this를 그대로 사용하기 위한 방법
2. self 등의 변수를 활용한 우회법보다 call, apply, bind를 사용하면 깔끔하게 처리 가능하다
var obj = {
outer: function() {
console.log(this); // obj
var innerFunc = function () {
console.log(this);
};
// call을 이용해서 즉시실행하면서 this를 넘겨주었습니다
innerFunc.call(this); // obj
}
};
obj.outer();
// bind 이용
var obj = {
outer: function() {
console.log(this);
var innerFunc = function () {
console.log(this);
}.bind(this); // innerFunc에 this를 결합한 새로운 함수를 할당
innerFunc();
}
};
obj.outer();
- 콜백함수
1. 콜백함수도 함수이기 때문에, 함수가 인자로 전달될 때는 함수 자체로 전달
2. bind메서드를 이용해 this를 입맛에 맞게 변경 가능
- 화살표 함수의 예외사항
- 화살표 함수는 실행 컨텍스트 생성 시, this를 바인딩하는 과정이 제외
- 함수 내부에는 this의 할당 과정이 아에 없으며, 접근하면 스코프체인상 가장 가까운 this에 접근하게 됨
- this우회, call, apply, bind보다 편리한 방법
var obj = {
outer: function () {
console.log(this);
var innerFunc = () => {
console.log(this);
};
innerFunc();
};
};
obj.outer();
'JavaScript Dev. > Javascript' 카테고리의 다른 글
14. DOM과 클래스, 클로저 (0) | 2023.10.28 |
---|---|
13. 콜백 함수와 동기/비동기 처리 (0) | 2023.10.23 |
11. 실행 컨텍스트(스코프, 변수, 객체, 호이스팅) (0) | 2023.10.11 |
10. 데이터 타입 (0) | 2023.04.21 |
2023.04.15 - Number vs. parseInt (1) | 2023.04.15 |