일반적인 객체지향 프로그램에서는 this는 인스턴스를 가르키게 되는데 자바스크립트에서는 다릅니다.
this는 기본적으로 실행 컨텍스트가 생성될 때 결정됩니다.
즉 함수가 호출될 때 실행컨텍스트가 생성되므로this는 함수를 호출할 때 결정됩니다.
어떤 방식으로 호출하느냐에 따라서 this의 값이 달라집니다.
브라우저에서는 window이고 node.js 환경에서는 global입니다.전역변수를 생성하게 되면 자바스크립트 엔진은 이를 전역 객체의 프로퍼티로도 할당하게 되는데 그 이유는?
자바스크립트의 모든 변수는 실은 특정 객체(렉시컬 환경)의 프로퍼티로서 동작합니다.
즉 실행 컨텍스트는 변수를 수집해서 렉시컬 환경의 프로퍼티로 저장하고
이후 어떤 변수를 호출하게 되면 렉시컬환경을 조회해서 있으면 반환하는 형식입니다.
둘의 차이점은 독립성입니다.
함수: 독립적인 기능을 수행메서드: 자신을 호출한 대상 객체에 관한 동작을 수행일반적으로 객체의 프로퍼티에 할당된 함수로 이해하곤 하는데 틀립니다.
어떤 함수를 개체의 프로퍼티에 할당한다고 해서 그 자체로서 메서드가 되는 것이 아니라 객체의 메서드로서 호출한 경우만 메서드로 동작하고 아니면 함수로 동작합니다.
let func = function(x){
console.log(this,x)
}
func(1). // Window{...} 1
let obj = {
method:func
};
obj.method(2) // {method: f} 2
다음과 같이 결괏값을 보면 함수는 그대로인데 어디서 부르냐에 따라 this가 달라지는 것을 볼 수 있습니다.
함수 앞에 점(.)이 있는지만으로 구분할 수 있습니다.
있으면 메서드, 없으면 함수입니다.
물론 대괄호 표기법에 따른 경우도 메서드로서 호출한 것입니다!
아래의 예시를 보면 마지막 점 앞에 명시된 객체가 this(호출한 주체)가 되는 것을 볼 수 있습니다!
let func = function(){
console.log(this)
}
let obj = {
methodA:func,
inner: {
methodB: func
}
};
obj.methodA() // obj
obj.inner.methodB() // obj.inner
this는 호출한 주체에 대한 정보가 담기는 것이므로
함수를 호출하는 것은 호출 주체를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이기 때문에 주체 정보를 알 수 없는 것입니다.
자바스크립트 특성상 함수 같은 경우는 그래서this는 전역 객체를 바인딩하게 됩니다.
let obj1 = {
outer: function() {
console.log(this);
var innerFunc = function() {
console.log(this);
}
innerFunc(); // Window{...}
let obj2 = {
innerMethod : innerFunc
};
obj2.innerMethod(); //{innerMethod: f}
}
}
obj1.outer(); // {outer: f}
다음과 같은 상황을 보면 알 수 있듯이 outer 내부에 있는 innerFunc를 함수로 호출한 경우와 메서드로
호출한 경우가 다른 거를 확인 할 수 있습니다.
이렇게 되면 this에 대한 구분은 명확해지지만 this가 주는 인상이 달라질뿐더러 자연스러워지지 않아집니다.
this를 바인딩 하지 않는 화살표 함수가 등장했습니다.화살표 함수는 실행컨텍스트를 생성할 때 this를 바인딩하는 과정 자체가 빠져서 상위스코프의 this를 활용하게 됩니다.let obj = {
outer: function() {
console.log(this);
let innerFunc = () => {
console.log(this);
};
innerFunc();// { outer: f}
}
}
obj.outer();// { outer: f}
그 밖에 방법으로 call,apply등의 메서드를 활용해서 명시적으로 this를 바인딩할 수 있습니다.
setTimeout(function () {
console.log(this);
}, 300) // window
[(1, 2, 3, 4, 5)].forEach(function (x) {
console.log(this, x); // window
});
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector("#a").addEventListener("click", function (e) {
console.log(this, e); // this 상속
});
setTimeout함수에서는 전역 객체인 Window가 출력forEach함수에서도 전역 객체인 Window가 출력addEventListener 에서는 자신의 this를 상속하도록 정의 따라서 콜백함수에서의
this는 콜백함수의 제어권을 갖는 함수(메서드)가this를 결정
new 명령어를 통해 함수를 호출하면 생성자로서 동작하게 됩니다.this가 인스턴스 자신이 됩니다.Function.prototype.call(thisarg [, arg1[, arg2[, ...]]])
Function.prototype.apply(thisarg [, argsArray])
메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령어로 this를 바인딩할 수 있습니다.
둘의 기능은 완전 동일하지만
call은 첫 번째 인자를 제외한 모든 인자를 호출할 함수의 매개변수로 지정
apply는 두번째인자를 배열로 받아 호출할 함수의 매개변수로 지정
let 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 }
let arr = Array.prototype.slice.call(obj); // [ 'a', 'b', 'c', 'd' ]
console.log(arr);
call과 apply메서드를 사용하여 배열 메서드를 사용할 수 있습니다.하지만 call/apply를 사용하여 형변환하는것은 ‘this를 원하는 값으로 지정해서 호출한다’라는
본래의 메서드의 의도와는 다소 동떨어진 활용법이라 ES6에서는 Array.from으로 배열로 전환하는 메서드가 생겼습니다.
Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])
call과 비슷하지만 즉시 호출하지 않고 넘겨받은this및 인수들을 바탕으로 새로운 함수를 반환
let func = function (a, b, c, d){
console.log(this, a, b, c, d);
}
func(1, 2, 3, 4); // Window{...} 1 2 3 4
let bindFunc1 = func.bind({ x: 1 });
bindFunc1(5, 6, 7, 8); // { x: 1 } 5 6 7 8
let bindFunc2 = func.bind({ x: 1 }, 4, 5);
bindFunc2(6, 7); // { x: 1} 4 5 6 7
bindFunc2(8, 9); // { x: 1} 4 5 8 9
예시와 같이 다시 새로운 함수를 호출할 때 인수를 넘기면
그 인수들은 기존 bind메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록됩니다.
let report = {
sum: 0,
count: 0,
add: function () {
let args = Array.prototype.slice.call(arguments); // 배열로 변환
args.forEach(function (entry) {
this.sum += entry;
++this.count;
}, this); // 콜백 함수 내부에서의 this가 해당 this로 바인딩
},
average: function () {
return this.sum / this.count;
},
};
report.add(60, 85, 95);
console.log(report.sum, report.count, report.average()); // 240 3 80
this로 지정할 객체(thisArg)를 인자로 지정할 수 있는 경우가 있습니다.forEach, map, filter, some, every, find, findIndex, flatMap, fromSet, Map