-보통 다른 객체지향 언어에서 this : 클래스로 생성한 인스턴스를 의미
1. 상황에 따라 this는 달라진다
❶전역 공간에서의 this = 전역 객체 -> 브라우저에서는 window / node환경에서는 global
❷메서드로서 호출 -> 그 메서드 내부에서의 this
※함수 vs 메서드
-서로 '독립성' 측면에서 다름
-함수는 스스로 수행이 됨(호출 주체가 없음) <-> 메서드는 어떤 객체가 실행시켜주어야 함 (호출 주체 있음)
=> ∴함수에서의 this -> 전역 객체 (∵호출의 주체를 명시할 수 없기 때문에) / 메서드에서의 this -> 호출의 주체 (객체)
❸ 함수로서 호출 -> 그 함수 내부에서의 this
-디폴트 : 함수이므로 this는 지정되지 않고(호출 주체가 없으니까), 실행컨텍스트 활성화 당시 this가 여전히 지정되어 있지 않다면 전역 객체를 의미
-메서드의 내부함수라면? : 얘도 '함수이므로' 예외없음
❗️아래 코드에서도 볼 수 있듯, this 바인딩 관련해서는 - 함수를 실행하는 당시의 주변환경(메서드 내부인지? 함수 내부인지?)가 중요한게 아니라, 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지가 관건이다.
var obj1 = {
outer: function() {
console.log(this); // (1)obj1
var innerFunc = function() {
console.log(this); // (2)전역객체, (3)obj2
}
innerFunc(); ⭐️
var obj2 = {
innerMethod: innerFunc
};
obj2.innerMethod(); ⭐️
}
};
obj1.outer(); ⭐️
❹콜백함수 호출 -> 그 콜백함수 내부에서의 this
-원래는 콜백 함수도 함수이므로, this가 전역객체로 갈아끼워지는 것이 맞는데
-.addEventListener처럼 특수한 경우 : 이 함수에서 자체적으로 별도로 this를 지정함(=button)
❺생성자 함수 내부에서의 this
*생성자: 구체적인 인스턴스(or...객체?)를 만들기 위한 일종의 틀
-this는 생성된 인스턴스를 가리킴 -> 인스턴스가 새로 생길 때마다, this도 새로이 가리킴
var Cat = function (name, age) {
this.bark = '야옹';
this.name = name;
this.age = age;
};
var choco = new Cat('초코', 7); //(bark는 야옹), name은 초코, age는 7인 인스턴스를 만들어냄
var nabi = new Cat('나비', 5);
2. 개발자들이 this를 우회하는 방법을 찾기 시작했다
❶변수를 활용하는 방법 (▵)
var obj1 = {
outer: function() {
console.log(this); // (1) outer
// AS-IS
var innerFunc1 = function() {
console.log(this); // (2) 원래는 innerFunc()호출과 동시에 this가 전역객체로 유실되었는데,
}
innerFunc1();
// TO-BE
var self = this; //흐름을 유지하고 있던 this를 변수 this에 저장하는 방법을 통해 => this를 계속 유지하는 것과 같은 .. 우회효과
var innerFunc2 = function() {
console.log(self); // (3) outer
};
innerFunc2();
}
};
// 메서드 호출 부분
obj1.outer();
❷함수 대신, 화살표 함수를 활용하는 방법
-기존 함수에서, this가 전역객체로 갈아끼워지는 유실 문제 발생 -> 이를 해결하기 위해 도입
-화살표 함수에서는 this binding 자체가 없음 -> ∴this는 이전의 상위값이 유지됨
var obj = {
outer: function () {
console.log(this); //obj
var innerFunc = () => {
console.log(this); //원래라면 this가 유실되었을텐데, 화살표함수여서 this가 obj를 가리킴
};
innerFunc();
};
};
obj.outer();
명시적 this 바인딩
-this에 별도의 값을 저장하는 방법
-크게 call, apply, bind가 있음
❶call
-호출 주체인 함수를 즉시 실행하는 명령어
-첫 번째 매개변수에 <- this로 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
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메서드와 완전 동일
-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가 있고, 인덱스가 있다.) 과 유사한 형태의 객체
-단, 객체에는 배열 메서드를 직접 적용할 수 없고(push, 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 메서드 (편리)
-알아서 객체 -> 배열로 만들어줌
// 유사배열
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
// 객체 -> 배열
var arr = Array.from(obj);
// 찍어보면 배열이 출력됨
console.log(arr);
❸-3. 생성자 내부에서 다른 생성자를 호출 (공통된 내용의 반복 제거)
-공통된 속성 : 별도로 생성자 함수를 만들어서 빼는게 간편
//Person 생성자 함수로 공통 속성들을 묶어줌
function Person(name, gender) {
this.name = name;
this.gender = gender;
}
//Student와 Emplyee의 공통된 속성은 name과 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를 사용
방법1.
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);
🤔Math.max.apply(null, numbers) ❓
-배열 numbers 안에서 최댓값을 찾는 방법
<Math.max 함수>
-인수로 받은 숫자들 중, 가장 큰 값을 반환
-BUT 개별적인 인수들을 받아들이기 때문에, 배열같은 데이터 구조는 직접 처리하지는 못함
-따라서 apply 메서드 등장
<apply 메서드>
-apply 메서드 : 배열을 Math.max 함수에 전달
-함수를 호출하면서 특정한 컨텍스트를 설정하고 + 배열 같은 인수들을 배열 요소로서 전달할 수 있게 해줌
1. 첫번째 인수로 - this값. (단, Math.max함수에서는 객체지향적 방법으로 사용되지 않으므로 null로 설정 : Math.max에서는 this값이 필요 없기 때문에 그냥 null로 설정해준 것)
2. 두번째 인수로 - numbers(배열) -> 배열의 각 요소를 인수로서 함수에 전달 -> Math.max함수가 그 중 최대값을 반환
방법2.
// 펼치기 연산자(Spread Operation) 사용
const numbers = [10, 20, 3, 16, 45];
const max = Math.max(...numbers); //배열을 풀어헤친 다음 -> 그 중에서 Math.max 실행
const min = Math.min(...numbers);
console.log(max min);
❹bind메서드
-call과 비슷
-but, 즉시 호출하지는 않음
-대신, 넘겨받은 this & 인수들을 가지고 -> 새로운 함수를 반환해주는 메서드
-함수에 this를 '미리' 적용하려 할 때나, 부분 적용 함수 구현 시 용이함
var func = function (a, b, c, d) {
console.log(this, a, b, c, d); //this는 global이 찍힘 -> 이걸 방지하기 위해
};
func(1, 2, 3, 4);
//1. 함수에 this를 미리 적용!
var bindFunc1 = func.bind({ x: 1 }); //this로 바인딩할 객체를 써줌 (this값이 x:1로 고정) -> 바인드 후 새로운함수를 리턴
bindFunc1(5, 6, 7, 8); //호출하면 결과가 5, 6, 7, 8이 아니라 {x:1}, 5, 6, 7, 8임
//2. 부분 적용 함수
var bindFunc2 = func.bind({ x: 1 }, 4, 5); //4와 5를 미리 적용한다는 뜻
bindFunc2(6, 7); //따라서 결과는 { x: 1 }, 4, 5, 6, 7
//+a) name 프로퍼티 : bindFunc1, 2처럼 bind메서드로 만든 함수는 name프로퍼티를 가지고 있음 : bound -> 추적하기가 쉬움
console.log(func.name); //func
console.log(bindFunc1.name); //bound func
console.log(bindFunc2.name); //bound func
call / apply / bind 방법 좀더 보기
//1.call 사용
var obj = {
outer: function () {
console.log(this);
var innerFunc = function () {
console.log(this);
};
//call을 이용 -> 즉시실행 -> this를 넘겨줌
innerFunc.call(this); //obj -> 그냥 innerFunc () 했었으면 전역객체로 유실되었을 것
},
};
obj.outer();
//2. bind 사용
var obj = {
outer: function () {
console.log(this);
var innerFunc = function () {
console.log(this);
}.bind(this); //1.function 뒤에 this를 미리 bind해줌 -> 나중에 어떤 식으로든 쓰기 위해
innerFunc(); //2.그다음 innerFunc에 this를 결합한 새로운 함수를 할당
},
};
obj.outer();
'개념 > Javascript(23.03.27~)' 카테고리의 다른 글
[Javascript] 형 변환 (0) | 2023.06.16 |
---|---|
[Javascript] DOM객체 (0) | 2023.06.01 |
[Javascript] 실행 컨텍스트 - 스코프, 변수, 객체, 호이스팅 (0) | 2023.05.26 |
[Javascript] 데이터 타입 - 메모리와 데이터, 변수 선언과 데이터 할당, 기본형 데이터와 참조형 데이터, 불변 객체, undefined와 null (0) | 2023.05.25 |
[Javascript] Map과 Set - 2.Set (0) | 2023.05.23 |