[JavaScript] this

Hyein Son·2020년 10월 27일
0

JavaScript

목록 보기
10/10

this

this는 실행 컨텍스트가 생성될 때 즉, 함수를 호출할 때 결정된다.

전역 공간에서의 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.

메서드 내부에서의 this

함수를 실행하는 방법에는 함수를 호출하는 경우메서드로서 호출하는 경우가 있다.
함수로 실행: 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가 전역객체를 참조하지만 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.
Ex) addEventListener의 경우 콜백 함수를 호출할 때 자신의 this를 상속하도록 정의되어 있기 때문에 메서드명의 점 앞부분이 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 바인딩 방법

call 메서드

첫 번째 인자: 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을 사용하면 임의로 바인딩할 객체를 정할 수 있다.

apply 메서드

첫 번째 인자: 바인딩할 객체, 두번째 인자: 배열

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 메서드를 도입했다.

여러 인수를 묶어 하나의 배열로 전달 - apply

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

bind 메서드

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();

콜백함수와 함께 thisArg를 인자로 받는 메서드

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에 접근한다.

0개의 댓글