this는 실행 컨텍스트가 생성될 때 즉, 함수를 호출할 때 결정된다.
전역 공간에서 this는 전역 객체를 가리킨다. 브라우저 환경에서 전역객체는 window이고 Node.js에서는 global이다.
자바스크립트의 모든 변수는 특정 객체의 프로퍼티로서 동작하는데, 전역 변수는 전역객체의 프로퍼티로 할당한다.
var a = 1;
console.log(a, window.a, this.a) // 1, 1, 1
var로 선언한 전역변수(var a)와 전역객체의 프로퍼티(window.a)는 호이스팅 여부, 변경 및 삭제 가능성에서 차이를 보인다.
var로 선언한 전역변수의 경우에는 delete메소드를 사용해도 삭제가 되지 않지만
var a = 1;
delete a; //false
delete window.a; //false
console.log(a, window.a) //1, 1
전역객체의 프로퍼티는 delete메소드를 사용할 경우 삭제가 된다.
window.b = 1;
delete b; //true
delete window.b; //true
console.log(b, window.b) //b is not defined.
함수를 실행하는 방법에는 함수를 호출하는 경우
와 메서드로서 호출하는 경우
가 있다.
함수로 실행: func()
/ 메서드로 실행: obj.method()
or obj['method']()
함수로 호출하면 this는 전역객체 window를 가리킨다. obj.method와 같이 메서드로서 호출하게 되면 this는 obj객체가 된다.
var obj = {
method: function () { console.log(this);},
}
obj.method(); // {method: function} === obj
함수로 호출하는 경우 메서드 내부함수의 this를 다음과 같이 사용할 수 있다.
var obj = {
outer: function(){
var inner1 = function(){
console.log(this); // window {...}
}
inner1();
var self = this;
var inner2 = function(){
console.log(self); // { outer : f }
}
inner2();
}
}
obj.outer();
outer함수 내부에서의 this(obj객체)를 변수 self에 저장해서 그 변수를 호출하면 상위스코프의 this를 사용할 수 있다.
콜백 함수는 기본적으로 this가 전역객체를 참조하지만 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.
Ex) addEventListener의 경우 콜백 함수를 호출할 때 자신의 this를 상속하도록 정의되어 있기 때문에 메서드명의 점 앞부분이 this가 된다.
var Cat = function (name, age){
this.bark = '야옹';
this.name = name;
this.age = age;
console.log(this) //Cat { bark: '야옹', name: 'cho', age: 30 }
}
var cho = new Cat("cho",30) //인스턴스 cho, 생성자 Cat
console.log(cho) // Cat { bark: '야옹', name: 'cho', age: 30 }
new 명령어와 함께 함수를 호출하면 해당 함수가 생성자로서 동작한다. 생성자 함수가 호출되면 this는 구체적인 인스턴스(객체) 자신이 된다.
첫 번째 인자: this 바인딩할 객체, 이후 인자: 함수의 매개변수
function.call(this 바인딩할 객체, 매개변수...)
var func = function (a, b, c) { console.log(this, a, b, c); } func(1, 2, 3); // window{...} 1 2 3 func.call({ x: 1 }, 4, 5, 6); // { x: 1 } 4 5 6
메서드로 호출하는경우 this는 객체를 참조하지만 call을 사용하면 임의로 바인딩할 객체를 정할 수 있다.
첫 번째 인자: 바인딩할 객체, 두번째 인자: 배열
function.apply(this 바인딩할 객체, [호출할 함수의 매개변수가 요소가 된다.])
var func = function (a, b, c) { console.log(this, a, b, c); } func(1, 2, 3); // window{...} 1 2 3 func.apply({ x: 1 }, [4, 5, 6]); // { x: 1 } 4 5 6
두 번째 인자인 배열이 호출할 함수의 매개변수의 개수보다 적으면 undefined를 나타내고, 많으면 매개변수의 개수만큼만 출력한다.
배열 메서드 push
를 사용하기 위해서 push뒤에 call
을 붙이면 객체에 push를 적용해 요소를 추가할 수 있다.
Array.prototype.push.call(유사배열객체, 추가할 요소) Array.prototype.push.apply(유사배열객체, [추가할 요소])
var obj = { 0: 'a', 1: 'b', 2: 'c', length: 3, }; Array.prototype.push.call(obj, 'd'); // { '0': 'a', '1': 'b', '2': 'c', '3': 'd', length: 4 } Array.prototype.push.apply(obj,['bb']); // { '0': 'a', '1': 'b', '2': 'c', '3': 'bb', length: 4 }
배열 메서드 slice
를 사용하면 객체를 배열로 전환할 수 있다.
Array.prototype.slice.call(유사배열객체, 시작 인덱스 값, 마지막 인덱스 값 + 1) Array.prototype.slice.apply(유사배열객체, [시작 인덱스 값, 마지막 인덱스 값 + 1])
Array.prototype.slice.call(obj); // ['a', 'b', 'c', 'd'] Array.prototype.slice.call(obj, 0, 1); // ['a'] Array.prototype.slice.apply(obj, [0, 1]); // ['a']
이외에도 pop, shift, unshift, splice, map, every, some 등 배열 메서드를 call / apply를 이용해 적용할 수 있다. (단, 문자열에 원본 데이터를 변경하는 배열 메서드(push, pop, shift, unshift, splice)를 적용하면 에러를 던진다.) 하지만 concat처럼 대상이 반드시 배열이어야 하는 경우에는 제대로 된 결과를 얻을 수 없다.
es6에서는 유사배열객체를 배열로 전환하는 Array.from
메서드를 도입했다.
Math.max / Math.min 메서드에 apply를 적용해 간결하게 작성할 수 있다.
var nums = [10, 20, 33, 2, 47];
var max = Math.max.apply(null, nums);
var min = Math.min.apply(null, nums);
console.log(max, min) // 47 2
call처럼 호출하지 않고 새로운 함수를 반환하기만 한다.
첫 번째 인자: 바인딩할 객체, 두 번째~ 인자: 함수에 넘길 인자 일부 지정
function.bind(this 바인딩할 객체, 호출할 함수의 매개변수)
var func = function (a,b,c,d) { console.log(this, a,b,c,d); } var bindFunc1 = func.bind({ x: 1 }); bindFunc1(5,6,7,8) // { x: 1 } 5 6 7 8
bind메서드와 call메서드를 사용해 외부 함수를 내부 함수의 this로 바인딩할 수 있다.
var obj = {
outer: function () {
console.log(this); // { outer: [Function: outer] }
var innerFunc = function () {
console.log(this); // { outer: [Function: outer] }
}.bind(this);
innerFunc();
}
};
obj.outer();
var obj = {
outer: function () {
var innerFunc = function () {
console.log(this); // { outer: [Function: outer] }
}
innerFunc.call();
}
}
obj.outer();
forEach, map, filter, some, every, find 등이 두번째 인자로 this를 받는다. 두번째 인자를 입력하면 콜백 함수 내부에서 this값을 변경할 수 있다.
var report = {
sum: 0,
count: 0,
add: function () {
var args = Array.prototype.slice.call(arguments);
args.forEach(function(entry){
console.log(this)
}, this); // { sum: 0, count: 0, add: [Function: add] }
},
}
report.add(60,85,95);
📌️ 화살표 함수
ES6에 새롭게 도입된화살표 함수
를 사용하면 위와 같은 this바인딩 과정이 필요없다. 화살표 함수 내부에는 this가 아예 없고, 접근했을 때는 스코프체인상 가장 가까운 this에 접근한다.