javascript - this 란...

김형석·2022년 8월 3일
0

개념공부 스터디

목록 보기
10/27

this 란?

this란 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(self-reference variable)이다.

인스턴스란?
비슷한 성질을 가진 여러개의 객체를 만들기 위해, 일종의 설계도라고 할 수 있는 생성자 함수(Constructor)를 만들어 찍어내듯 사용하는데 이렇게 생성된 객체를 인스턴스라 부를 수 있다.

this 는?

this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.

this는 객체와 연관이 깊다.

var a = { name : "wecode" }

객체가 있고, 특정 상황을 만족할 경우 this.name을 호출하면 "wecode"가 출력된다.
이때 a를 this의 콘텍스트(context) 객체라고 할 수 있다.

콘텍스트(context) 객체
this가 바라보고 있는 어떤 객체 정도로 생각하면 이해하기 쉽다.

this의 4가지 동작 방식에 대해 살펴보자.

바인딩
식별자와 값을 연결하는 과정을 말한다.
변수선언은 변수 이름과 확보된 메모리 공간의 주소를 바인딩하는 것이다.
this 바인딩은 this(키워드로 분류되지만 식별자의 역할을 한다.)와 this가 가리킬 객체를 바인딩하는 것이다.

1. this의 첫번째 동작 방식(전역객체)

개발자 도구의 콘솔에서 console.log(this)를 쳐보자.

javascript의 실행환경 전역 객체가 나온다. 즉, this의 첫번째 동작 방식은 this가 전역 객체(window)를 context 객체로 갖는 것이다.

var g = 20;
console.log(this.g); // 20

function doSomething() {
  this.dummy2 = "가을";
  console.log(this); // window 객체
}

console.log(this.dummy1); // undefined
console.log(this.dummy2); // undefined

this.dummy1 = "겨울";

console.log(this.dummy1); // 겨울
console.log(this.dummy2); // undefined

doSomething();

console.log(this.dummy1); // 겨울
console.log(this.dummy2); // 가을

전역 스코프에서 정의한 변수들은 전역 객체에 등록이 된다.

var g = 20은 window.g = 20과 같고,
this 객체에 프로퍼티(dummy1, dummy2)를 등록하는 것은 사실상 전역 스코프에서 변수를 선언한 것과 같다는 이야기이다.

추가로 전역 객체는 우리가 알고 있는 일반 객체(object)처럼 아무 제약 없이 동작하기 때문에 this를 무분별하게 사용할 경우, 전역 상태에 영향을 끼칠 수 있으므로 주의해야한다.

2. 암시적 바인딩

function test() {
  console.log(this.a);
}

var obj = {
  a: 20,
  func1: test,
  func2: function() {
    console.log(this.a);
  }
};

obj.func1(); // 20
obj.func2(); // 20

어떤 객체를 통해 함수가 호출된다면 그 객체가 바로 this의 context 객체가 된다.
위 코드의 func1, func2는 obj를 통해 호출되었으므로, obj가 this가 된다는 뜻이다.

var b = 100;

function test() {
  console.log(this.b);
}

var obj = {
  a: 20,
  func1: test,
  func2: function() {
    console.log(this.b);
  }
};

obj.func1(); // undefined
obj.func2(); // undefined

var gFunc1 = obj.func1;
gFunc1(); // 100

test 함수 내 this.a가 this.b로 변경되었다.

암시적 바인딩의 첫번째 예제를 보고 온 우리의 obj.func1과 obj.func2는 unfdefined를 출력하는게 이해되지만, gFunc1은 100을 왜 출력할까?

첫번째 동작 방식에서 보았듯이, 전역 스코프에서 생성한 변수는 전역 객체에 등록이 되기 때문이다.

var b = 100;

function test() {
  console.log(this.b);
}

var obj = {
  a: 20,
  func1: test,
  func2: function() {
    console.log(this.b);
  }
};

obj.func1(); // undefined
obj.func2(); // undefined

this.gFunc1 = obj.func1; // 실상 전역객체에 프로퍼티를 등록한 것과 같다 -> window.gFunc1 = obj.func1과 같다
this.gFunc1(); // 100

다른 예도 보자.

function test() {
  console.log(this.b);
}

var obj1 = {
  b: 10,
  func: test
};

var obj2 = {
  b: 40
};

obj2.func = obj1.func; // var func = obj1.func 와 같다
obj2.func(); // 40

결론적으로 암시적 바인딩을 하게되는경우 함수호출 위치에 따라서 런타임 바인딩이 되기때문에 원하는 값을 얻지 못하는 경우가 있다.

3. 명시적 바인딩

명시적 바인딩은 암시적 바인딩의 반대의 의미라고 생각하면 된다.
명시적 바인딩은 말 그대로 직접적으로 명확하게 어떠한 객체를 참조하라는 의미를 부여하는 것이다.

apply(), bind(), call() 프로토타입 메서드를 사용하여 함수를 호출하면 this를 지정할 수 있어서 명시적 바인딩에 해당된다.

/* 
function A(_this){
	return _this.a;
}
*/

function A() {
    return this.a;
}

var obj = {
    a: 2
};

var result = A.call( obj );

console.log(result); // 2

함수 A를 만들어서 함수 호출하는 코드이다.
A라는 함수를 일반적을 호출하지 않고 call이라는 걸 붙여서 호출했다.
A함수에 매개변수로 받을 만한 인자 값이 없이 call 메서드를 이용하여 obj라는 객체를 매개변수에 넣어서 호출했다.

이게 바로 명시적으로 A함수에게 obj 객체의 this를 지정해주는 것이다.
즉, A함수 내에서 this는 obj가 되는 것이다.

function A() {
    console.log(this); // 무엇이 찍힐까?
}

var obj = {
    a: 2
};

A.call( obj );

그렇다면 위 코드는 무엇이 찍힐까?

암시적 바인딩은 위에서 설명했듯이 this 값이 암시적으로 지정되고, this참조가 바뀌는 반면에 명시적 바인딩은 직접적으로 this에 이 객체를 참조하라고 지정을 해주기 때문에 차이점을 알고 있어야 한다.

1. call 메서드

call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령어이다. 이때 call 메서드의 첫 번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 매개변수로 한다. 즉, 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 메서드를 사용하면 임의의 객체를 this로 지정할 수 있다.

var obj = {
	a: 1,
    method: function(x, y) {
    	console.log(this.a, x, y)
    }
}

obj.method(2, 3); // 1 2 3
obj.method.call({ a : 4 }, 5, 6) // 4 5 6

2. apply 메서드

apply 메서드는 call 메서드와 기능적으로 동일하지만 매개변수를 지정하는 방식에서 차이가 존재한다. call메서드는 첫 번째 인자를 제외한 나머지 모든 인자들을 호출할 함수의 매개변수로 지정하는 반면, apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다.

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

bind 메서드

call 과 비슷하지만 즉시 호출하지 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드이다. 다시 새로운 함수를 호출할 때 인수를 넘기면 그 인수들은 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록된다.
즉, bind메서드는 함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 두 가지 목적을 모두 지닌다.

var func = function(a, b, c, d) {
	console.log(this, a, b, c, d);
}
func(1, 2, 3, 4); // Window{ ... } 1 2 3 4

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);
bindFunc2(6, 7); // { x: 1 } 4 5 6 7
bindFunc2(8, 9); // { x: 1 } 4 5 8 9

4. new 바인딩

function Foo(a) {
  // this = {};  (빈 객체가 암시적으로 만들어짐)
  
  // 새로운 프로퍼티를 this에 추가함
  this.a = a;
  this.qwer = 20;
  
  // return this;  (this가 암시적으로 반환됨)

}

var bar = new Foo(2);
console.log(bar.a); // 2
console.log(bar.qwer); // 20

new 바인딩의 동작 순서

  1. 새 객체가 만들어짐
  2. 새로 생성된 객체의 Prototype 체인이 호출 함수의 프로토타입과 연결됨
  3. 1에서 생성된 객체를 this context 객체로 사용(명시적으로)하여 함수가 실행됨
  4. 이 함수가 객체를 반환하지 않는 한 1에서 생성된객체가 반환됨

this 란?
this란 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(self-reference variable)이다.

Arrow function

화살표 함수에서는 this는 특별하게 작동한다.

내부에서 this 값을 쓸 때 밖에 있던 this값을 그대로 사용한다.(이게 장점이자 arrow function을 쓰는 핵심 이유)

각각 무엇이 출력되는지 생각해보자.

var 오브젝트1 = {
  함수 : function(){ console.log(this) }
}

오브젝트1.함수()
var 오브젝트1 = {
  함수 : () => { console.log(this) }
}

오브젝트1.함수()

arrow function은 외부에 있던 this를 그대로 내부로 가져와서 사용하는 함수이다.

항상 장점은 아니다. 내가 예측하던 this값과 달라질 수도 있으니 단점이 될 수 있으니 잘 쓰자.

profile
블로그 이사 : https://hengxi.tistory.com

0개의 댓글