study: javascript | 숨참고 deep dive (22) this

Lumpen·2023년 3월 15일
0

Study

목록 보기
23/92

this 키워드

객체는 상태와 동작을 논리적 단위로 묶은 복합적 자료구조다

동작을 나타내는 메서드는 자신이 속한 객체의
상태(프로퍼티)를 참조하고 변경할 수 있어야 한다
떄문에 자신이 속한 객체를 가리키는 식별자가 필요하고 이게 this 임

객체 리터럴 방식으로 생성한 객체의 경우
메서드 내부에서 메서드 자신이 속한 객체를 가리키는 식별자를
재귀적으로 참조할 수 있다

const circle = {
	radius: 5,
  	getDiameter() {
    	return 2 * circle.radius
    }
}

위에서 circle 은 getDiameter 라는 메서드 내부에서
자신의 이름으로 자신을 참조하고 있다
이 참조 표현식은 getDiameter 가 호출되어 함수 몸체가 실행되는 시점에 평가된다
객체 리터럴은 circle 변수에 할당되기 직전에 평가된다
따라서 getDiameter 메서드가 호출되는 시점에는
이미 객체 리터럴의 평가가 왼료되어 객체가 생성되었고
circle 식별자에 생성된 객체가 할당된 이후다
따라서 메서드 내부에서 circle 식별자를 참조할 수 있는 것

하지만 자신이 속한 객체를 재귀적으로 참조하는 위 같은 방식은 바람직하지 않다

이 때 circle 대신 this 키워드를 사용

function Circle(radius) {
  	// 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다
	this.radius = radius
}

Circle.prototype.getDiameter = function () {
 	// 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다
	return 2 * this.radius
}

// 인스턴스를 생성해야 비로소 자기 자신을 가리키는 식별자를 알 수 있게 된다
const circle = new Circle(5)

this 는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수다
this 는 자바스크립트 엔진에 의해 암묵적으로 생성되면 코드 어디서든 참조할 수 있다
함수 호출 시 arguments 객체와 this 가 암묵적으로 함수 내부에 전달된다
this 도 지역 변수처럼 사용할 수 있다

this 가 가리키는 값, this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다
이는 자바스크립트만이 가지는 특징

바인딩이란 식별자와 값을 연결하는 과정을 말한다 
this 는 키워드로 분류되지만 식별자 역할을 한다

strict mode 에서는 일반함수의 this 에 undefined 가 바인딩 된다
일반 함수는 this 를 사용할 필요가 없기 떄문이다

함수 호출 방식과 this 바인딩

this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다

렉시컬 스코프와 this 바인딩은 결정 시기가 다르다

함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프는 함수 정의가 평가되어 
함수 객체가 생성되는 시점에 상위 스코프를 결정한다
하지만 this 바인딩은 함수의 호출 시점에 결정된다

함수 호출 방식

  • 일반 함수 호출
  • 메서드 호출
  • 생성자 함수 호출
  • Function.prototype.apply/call/bind 메서드에 의한 간접 호출

일반 함수 호출

일반 함수 호출 시 기본적으로는 this 에 전역 객체가 바인딩 된다
전역 객체는 자바스크립트의 실행 환경에 따라 window, global 로 참조할 수 있고
때문에 strict mode 에서는 this 에 undefined 가 바인디 ㅇ된다

충첩 함수의 경우에도 일반 함수로 호출되면 this에 전역 객체가 바인딩 된다

콜백 함수가 일반 함수로 호출된다면 콜백함수 내부의 this 에도 전역 객체가 바인딩 된다

일반함수로 호출되는 모든 함수에 this 는 전역 객체가 바인딩 된다

메서드 호출

메서드 내부의 this 에는 메서드를 호출한 객체
즉 메서드 이름 앞의 마침표 연산자 앞에 기술한 객체가 바인딩 된다

메서드 내부의 this 는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩 된다

const person = {
	name: 'Lee',
  	getName() {
    	return this.name
    }
}

// getName 을 호출한 객체는 person 이므로 this 가 person 에 바인딩
console.log(person.getName()) // Lee

getName 프로퍼티가 가리키는 함수 객체는
person 객체에 포함된 것이 아니라 독립적으로 존재하는 별도의 객체다
getName 프로퍼티가 함수 객체를 가리키고 있을 뿐이다

getName 프로퍼티가 가리키는 함수 객체, 즉 getName 메서드는
다른 객체의 프로퍼티에 할당하는 것으로
다른 객체의 메서드가 될 수도 있고
일반 변수에 할당하여 일반 함수로 호출될 수도 있다

const anotherPerson = {
	name: 'Kim'
}

anotherPerson.getName = person.getName
console.log(anoterPerson.getName()) // Kim

const getName = person.getName
console.log(getName) // '' 
// this.name 은 브라우저 환경에서 window.name 과 같다
// window.name 은 브라우저 창의 이름을 나타내는 빌트인 프로퍼티로 기본 값이 ''

메서드 내부의 this 는 프로퍼티로 메서드를 가리키고 있는 객체와는 관계가 없고
메서드를 호출한 객체에 바인딩 된다

프로토타입 메서드 내부에서 사용된 this 도 일반 메서드와 마찬가지로
해당 메서드를 호출한 객체에 바인딩 된다

생성자 함수 호출

생성자 함수 내부의 this 는 생성자 함수가 생성하는 인스턴스에 바인딩된다

간접 호출

Function.prototype.apply
Function.prototype.call
Function.prototype.bind
를 사용하여 this 를 간접 호출할 수 있다
이들 메서드는 모든 함수가 상속받아 사용할 수 있다

apply 와 call 메서드의 본질적인 기능은 함수를 호출하는 것이다
apply 와 call 메서드는 함수를 호출하면서
첫 번쨰 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩 한다

function getThisBinding() {
	return this
}

const thisArg = { a: 1 }

console.log(getThisBinding()) // window

console.log(getThisBinding.apply(thisArg)) // { a: 1 }
console.log(getThisBinding.call(thisArg)) // { a: 1 }

둘의 차이는 전달될 매개변수의 생김새가 다르다는 것

Function.prototype.bind 메서드는
함수를 호출하지 않고 this 로 사용할 객체만 전달한다

function getThisBinding() {
	return this
}

const thisArg = { a: 1 }
console.log(getThisBinding.bind(thisArg)) // getThisBinding
console.log(getThisBinding.bind(thisArg)()) // { a: 1 }

bind 메서드는 함수를 호출하지는 않으므로 명시적 호출을 해야한다

bind 메서드는 메서드의 this 와 내부의 중첩함수, 콜백함수의 this 가
불일치하는 문제를 해결하기 위해 유용하게 사용된다

화살표 함수

화살표 함수에는 this value 가 존재하지 않기 때문에
함수 내에서 this 를 사용하면
스코프 체인에 의해 자신이 속해있는 상위 스코프의 this 를 참조하게 된다

profile
떠돌이 생활을 하는. 실업자는 아니지만, 부랑 생활을 하는

0개의 댓글