<aside> 💡 - 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
</aside>
함수 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
위의 내용에서 보았듯, 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
함수로서 호출할 때 그 함수 내부에서의 this
함수 내부에서의 this
독립적으로
호출할 때는 this는 항상 전역객체를 가리킨다
는 것을 주의하자.메서드의 내부함수에서의 this
예외는 없다. 메서드의 내부라고 해도, 함수로서 호출한다면 this는 전역 객체를 의미한다.
var obj1 = {
outer: function() {
console.log(this); // obj1
var innerFunc = function() {
console.log(this); // (2), (3)
}
innerFunc(); // 여기서 출력한 this (2) -> window
var obj2 = {
innerMethod: innerFunc
};
obj2.innerMethod(); // 여기서 출력한 this (3) -> obj2
}
};
obj1.outer();
<aside>
💡 this 바인딩에 관해서는 함수를 실행하는 당시의 주변 환경 (메서드 내부인지, 함수 내부인지)는 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지
가 관건이라는 것을 알 수 있다.
</aside>
메서드의 내부 함수에서의 this 우회
this는 상당히 애매모호하고 받아들이기가 어려운 점이 많다. 그렇기에 this를 우회할 수 있는 방법을 찾아볼 수 있다.
변수를 활용하는 방법
내부 스코프에 이미 존재하는 this를 별도의 변수 (ex: self)
에 할당하는 방법.
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를 바인딩하지 않는 함수)
ES6에서 처음 도입된 화살표 함수는, 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 없다. 따라서 this는 이전의 값 - 상위 값 - 이 유지된다. / ES6에서는 함수 내부에서 this가 전역 객체를 바라보는 문제 때문에 화살표 함수를 도입했다.)
일반 함수와 화살표 함수의 가장 큰 차이점은 무엇일까?
→ 바로 this binding 여부가 가장 적절한 답변.
var obj = {
outer: function() {
console.log(this); // (1) obj
var innerFunc = () => {
console.log(this); // (2) obj
};
innerFunc();
}
}
obj.outer();
콜백 함수 호출 시 그 함수 내부에서의 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);
});
생성자 함수 내부에서의 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
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
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 메서드 활용
this binding을 위해 call, apply 메서드를 활용하기도 하지만 더 유용한 측면이 있다.
유사 배열 객체 (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' ]
Array.from 메서드 (ES6)
bind 메서드
화살표 함수의 예외사항