자바스크립트에서는 this가 어디에서나 사용될 수 있다.상황에 따라 달라지는 this
this는 함수를 호출할 때 결정된다. 라고 할 수 있다.런타임 환경에 따라 this는 window(브라우저 환경) 또는 global(node 환경)를 각각 의마한다.<브라우저 환경 this 확인 예시>
console.log(this);
console.log(window);
console.log(this === window); // true 출력
<node 환경 this 확인 예시>
console.log(this);
console.log(global);
console.log(this === global); // true 출력
독립성이디. 함수명();
<메서드가 자신을 호출한 대상 객체에 대한 동작을 수행한 예시>
객체.메서드명();
<예시>
// 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
호출을 누가 했는지에 대한 정보가 저장된다.<예시>
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
this는 항상 전역객체를 가리킨다는 것을 주의해야 한다!!<예시>
var obj1 = {
outer: function() {
console.log(this); // (1)
var innerFunc = function() {
console.log(this); // (2), (3)
}
innerFunc();
var obj2 = {
innerMethod: innerFunc
};
obj2.innerMethod();
}
};
obj1.outer();
☆ this 바인딩에 관해서는 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지)은 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지가 관건이라는 것을 알 수 있다.
<예시>
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();
this가 전역객체를 바라보는 문제 때문에 화살표함수를 도입했다! this binding 여부이다.<예시>
var obj = {
outer: function() {
console.log(this); // (1) obj
var innerFunc = () => {
console.log(this); // (2) obj
};
innerFunc();
}
}
obj.outer();
콜백 함수 정의
“어떠한 함수, 메서드의 인자(매개변수)로 넘겨주는 함수”
콜백함수 내부의 this는 해당 콜백함수를 넘겨받은 함수(메서드)가 정한 규칙에 따라 값이 결정된다.
콜백 함수도 함수기 때문에 this는 전역 객체를 참조하지만(호출 주체가 없다),
단, 콜백함수를 넘겨받은 함수에서 콜백 함수에 별도로 this를 지정한 경우는 예외적으로 그 대상을 참조하게 되어있다.
<예시>
☆ 예시를 통해 로직을 이해하는 것 보다는, 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);
});
<예시>
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
명시적 this 바인딩
call / apply / bind가 있다.명시적으로 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
<예상되는 this가 있음에도 일부러 바꾸는 예시>
var obj = {
a: 1,
method: function (x, y) {
console.log(this.a, x, y);
}
};
// method함수 안의 this는 항상 obj!!
obj.method(2, 3); // 1 2 3
obj.method.call({ a: 4 }, 5, 6); // 4 5 6
<예시>
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
반드시 length가 필요 하다. 이 조건은 필수이며, 없으면 유사배열이라고 인식하지 않는다.
index번호가 0번부터 시작해서 1씩 증가해야하한다, 아니어도 되긴 하지만 예상치 못한 결과가 발생한다.
<예시>
//객체에는 배열 메서드를 직접 적용할 수 없다.
//유사배열객체에는 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' ]
<Array.from 방법 예시>
// 유사배열
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
// 객체 -> 배열
var arr = Array.from(obj);
// 찍어보면 배열이 출력된다.
console.log(arr);
<예시>
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', '삼성');
Student, Employee 모두 Person이다.Student와 Employee 인스턴스를 만들 때 마다 세 가지 속성을 모두 각 생성자 함수에 넣기 보다는 Person이라는 생성자 함수를 별도로 빼는게 ‘구조화’에 더 도움이 된다. <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);
<apply를 적용한 예시>
//효율
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);
미리 적용한다!부분 적용 함수 구현할 때 용이하다.<예시>
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
<예시>
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
<내부함수에 this를 전달하기 위해 self를 썼었던 에시>
//TO-BT
var self = this;
var innerFunc2 = =function() {
console.log(self); // 93) outer
};
innerFunc2();
<예시>
var obj = {
outer: function() {
console.log(this); // obj
var innerFunc = function () {
console.log(this);
};
// call을 이용해서 즉시실행하면서 this를 넘겨주었다.
innerFunc.call(this); // obj
}
};
obj.outer();
<call이 아니라 bind를 이용한 예시>
var obj = {
outer: function() {
console.log(this);
var innerFunc = function () {
console.log(this);
}.bind(this); // innerFunc에 this를 결합한 새로운 함수를 할당
innerFunc();
}
};
obj.outer();
콜백함수도 함수이기 때문에, 함수가 인자로 전달될 때는 함수 자체로 전달된다. (this가 유실된다!)<예시>
var obj = {
logThis: function () {
console.log(this);
},
logThisLater1: function () {
// 0.5초를 기다렸다가 출력한다. 정상동작하지 않는다.
// 콜백함수도 함수이기 때문에 this를 bind해주지 않아서 유실했다.
setTimeout(this.logThis, 500);
},
logThisLater2: function () {
// 1초를 기다렸다가 출력한다. 정상동작한다.
// 콜백함수에 this를 bind 해주었기 때문이다.
setTimeout(this.logThis.bind(this), 1000);
}
};
obj.logThisLater1();
obj.logThisLater2();
<예시>
var obj = {
outer: function () {
console.log(this);
var innerFunc = () => {
console.log(this);
};
innerFunc();
};
};
obj.outer();