this가 가리키는 것
JS의 this는 런타임 상황에서 고려해야한다. 따라서 두가지 상황을 고려할 수 있다. node, 브라우저!
1) 브라우저: window 객체
2) 노드: global 객체
메서드 내부에서의 this
메서드와 함수
함수는 그 자체로서 독립적인 기능을 수행하지만, 메서드는 자신을 호출한 대상 객체에 대한 동작을 수행한다.
1) 함수의 this: 전역 객체
2) 메서드의 this: 호출의 주체
this의 할당
function exFunc() {
console.log("this: ", this);
}
exFunc(); // this: <ref *1> Object [global]
const obj = {
method: exFunc,
}
obj.method(); // this: { method: [Function: exFunc] } -> exFunc가 포함된 객체 obj
같은 exFunc()를 사용하더라도 독립적으로 호출했을 때, obj의 메서드로서 호출했을 때 this가 의미하는 바가 다르다.
함수로서 호출 시 그 함수 내부에서의 this
메서드 내부에서 함수를 호출하더라도, 그 함수는 독립적으로 호출된 것이기 때문에 this는 전역객체(global, window)다.
const obj1 = {
outer: function() {
console.log(this); // obj1
const innerFunc = function() {
console.log(this);
}
innerFunc(); // global (주체없이 독립적으로 호출됐기 때문에)
const obj2 = {
innerMethod: innerFunc,
};
obj2.innerMethod(); // obj2
}
};
obj1.outer();
메서드 내부 함수에서의 this 우회
변수를 활용하는 방법 (잘 사용하지 않음)
내부스코프에 이미 존재하는 this를 별도의 변수에 할당하는 방법
const obj1 = {
outer: function() {
const self = this;
const innerFunc = function() {
console.log(self); // obj1
}
innerFunc();
}
};
obj1.outer();
화살표 함수 (this를 바인딩하지 않음)
const obj1 = {
outer: function() {
const innerFunc = () => {
console.log(this); // obj1
}
innerFunc();
}
};
obj1.outer();
콜백함수 호출 시 그 함수 내부에서의 this
콜백함수도 함수다. 따라서 this는 전역객체를 참조하지만, 콜백함수를 넘겨받은 함수에서 별도로 this를 지정하는 경우는 예외적으로 그 대상을 참조한다.
ex. addEventListener → 콜백함수에 이벤트가 발생한 element가 this로 지정됨
생성자함수 내부에서의 this
생성자
구체적인 인스턴스를 만들기 위한 일종의 틀
const Cat = function (name, age) {
this.bark = '야옹';
this.name = name;
this.age = age;
};
const choco = new Cat('초코', 7); // this: choco
const nabi = new Cat('나비', 5); // this: nabi
생성자 함수 내부에서의 this는 생성하는 인스턴스를 의미한다!
명시적 this binding
this === global 인 경우
const func = function (a, b, c) {
console.log(this, a, b, c);
};
func(1, 2, 3); // global
바인딩하지 않은 경우 this === global인 func
const func = function (a, b, c) {
console.log(this, a, b, c);
};
func.call({ x: 1 }, 1, 2, 3); // { x: 1 }
const obj = { name: 'obj' }
func.call(obj, 1, 2, 3); // obj
.call을 사용해 this로 지정하고싶은 객체를 첫번째 인자로 넘겨준다!
호출 주체가 있는 경우
const obj = {
a: 1,
method: function () {
console.log("this: ", this);
},
};
obj.method(); // obj
const obj = {
a: 1,
method: function () {
console.log("this: ", this);
},
};
const otherObj = { }
obj.method.call(otherObj); // otherObj
apply
.call()과 동일한 역할! 뒤의 매개변수들만 배열로 묶어서 보내주면 된다.
const obj = {
a: 1,
method: function (x, y) {
console.log("this: ", this, x, y);
},
};
const otherObj = { }
obj.method.apply(otherObj, [1, 3]); // otherObj 1 3
call, apply 활용해 유사배열객체 활용하기
유사 배열 객체
const obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
};
위와 같이 실제 배열은 아니지만 인덱스를 갖고있고, 길이(length) 속성을 갖고있는 객체
유사배열객체는 형태만 유사할뿐 당연히 배열의 메서드들을 사용할 수 없다. 하지만 call, apply를 활용하면 배열 메서드를 차용할 수 있다!
Array.prototype.push.call(obj, 'd');
console.log(obj); // { 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }
Array.from()
사실 위의 방법은 바인딩의 목적에 들어맞지는 않는다. 따라서 ES6에서 위 동작을 수행할 수 있는 메서드가 제안됐다!
const arr = Array.from(obj);
console.log(arr); // ['a', 'b', 'c', 'd']
Call, apply 활용
1) 공통된 코드 효율적으로 사용하기
function Student(name, age, school) {
this.name = name;
this.age = age;
this.school = school;
}
function Employee(name, age, company) {
this.name = name;
this.age = age;
this.company = company;
}
이와 같이 사람이라는 큰 맥락에서 세부적으로 학생, 직장인으로 나뉠 수 있고 구체적인 내용까지 유사한 생성자가 있다.
function Person(name, age) {
this.name = name;
this.age = age;
}
function Student(name, age, school) {
Person.call(this, name, age);
this.school = school;
}
function Employee(name, age, company) {
Person.call(this, name, age);
this.company = company;
}
let st = new Student('Kim', 10, 'ㅇㅇ초등학교');
let em = new Employee('Lee', 30, 'ㅇㅇ기업');
call을 사용해 생성자 내부에서 this를 바인딩해준다. 생성자에서의 this는 새로 만들어지는 인스턴스이므로 st, em의 이름과 나이를 설정할 수 있다.
2) 배열에서 최소, 최대값 구하기
const numbers = [10, 2, 8, 16, 3];
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);
// ---- 추가 (spread operator)
// Math.max와 Math.min은 매개변수로 들어온 수들의 최대/최소를 구하는 것이므로 배열 형태를 벗어나 값들만 남도록
// spread operator를 사용할 수도 있다.
const spreadMax = Math.max(...numbers);
const spreadMin = Math.min(...numbers);
bind()
this를 바인딩하는 다른 메서드. call, apply와의 차이는 즉시 호출하지 않는다는 점이다.
활용 목적
1) 함수에 this 를 미리 적용
const func = function () {
console.log(this);
};
const bind1 = func.bind({x : 1});
// 생략
bind1(); // { x : 1 }
this를 미리 적용해두고 사용할 수 있다!
2) 부분 적용 함수
const func = function (a, b, c, d) {
console.log(this, a, b, c, d);
};
const bind2 = func.bind({x : 1}, 10, 11);
// 생략
bind1(5, 6); // { x : 1 } 10 11 5 6
매개변수의 일부만 지정해줄 때 사용할 수 있다.
name property
this binding한 함수는 bound라는 name property 값을 갖고있다.
상위 컨텍스트의 this를 내부함수나 콜백함수에 전달하기
위의 변수를 활용한 우회적 전달도 가능하지만, call/apply/bind를 활용해 함수 호출시 바인딩 해주는 것이 좋다!
const obj = {
outer: function() {
console.log(this); // obj
const innerFunc = function() {
console.log(this);
}
}
// 즉시 실행하며 바로 this 전달
innerFunc.call(this); // obj
}
obj.outer();
const obj = {
outer: function() {
console.log(this); // obj
const innerFunc = function() {
console.log(this);
}.bind(this); // innerFunc에 this를 결합한 새로운 함수 할당
innerFunc(); // obj
}
};
obj.outer();
화살표 함수도 활용할 수 있다~
const obj = {
outer: function() {
console.log(this);
const innerFunc = () => {
console.log(this); // this 바인딩 과정이 없음
};
innerFunc(); // obj
};
};
obj.outer();
화살표 함수는 마지막에 바인딩된 this를 가리킨다. 따라서 상위 컨텍스트의 this!
'JavaScript' 카테고리의 다른 글
[JS] 콜백지옥 해결방안 (기명함수, promise, generator, async/await) (0) | 2024.07.24 |
---|---|
[JS] 콜백함수의 제어권 (0) | 2024.07.24 |
[JavaScript] 실행 컨텍스트 및 콜스택 (0) | 2024.07.23 |
[JavaScript] 깊은 복사와 얕은 복사 (0) | 2024.07.23 |
[JavaScript] Map과 Set (0) | 2024.07.22 |