이제 슬슬 모던 자스의 활약상이 나올 때다 !
앞으로 this, 실행 컨텍스트, 클로저, 클래스가 쭉쭉 나온다 .. (후)
그래도 this에 대해선 많은 지식을 쌓았으니 ! 정리하는 느낌으로 가보자고 !
this
키워드메서드는 프로퍼티를 참조하고 변경하는 역할 수행을 위해, 자신이 속한 객체를 가리키는 식별자를 참조해야만 한다.
객체 리터럴 방식
으로 생성한 객체의 경우,
메서드 내부에서 자신이 속한 객체를 가리키는 식별자를 재귀적으로 사용할 수 있다.
const circle(){
radius: 5,
getDiameter(){
return 2 * circle.radius; // 재귀적 사용
}
}
객체 리터럴은 circle 변수에 할당하기 이전에 평가된다. (객체가 먼저 평가되고 할당되니까)
따라서 getDiameter 메서드가 호출되는 시점에는 이미, 진즉에 객체 리터럴 평가 완료, 객체 생성, circle 식별자에 할당까지 모두 완료된 이후이다. getDiameter
에서 circle 식별자
를 참조할 수 있는 이유가 바로 이것이다.
하지만 자신이 속한 객체를 재귀적으로 참조하는 방식은 권장되지 않는다. (비추)
객체 리터럴이 아닌 생성자 함수 방식으로 인스턴스를 생성하는 경우를 살펴보자.
생성자 함수로 만들어진 인스턴스(객체)를 재귀적으로 참조하는 방식은 불가능하다.
인스턴스를 만들기 이전에 생성자 함수를 정의하는 과정이 우선되어야 하기 때문이다.
인스턴스가 만들어지지 않은, 생성자 함수가 생성되는 시점에서 인스턴스를 가리키는 식별자를 알 방법은 없다.
this
줄게따라서 자바스크립트는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 특수한 식별자, this
를 제공한다.
this
는 자신이 속한 객체나 생성할 인스턴스를 가리키는 자기 참조 변수this
는 자신이 속한 객체나 생성할 인스턴스의 프로퍼티/메서드 참조 가능this
는 암묵적으로 생성되고 어디서든 참조 가능함수를 호출하면 arguments
객체와 this
가 암묵적으로 함수 내부에 전달된다.
arguments
객체처럼 this
도 동일하게 함수 내부에서 지역 변수로 사용이 가능하다.
다만 this
가 가리키는 값(this 바인딩)은 함수 호출 방식에 의해 동적으로 결정된다. (이것이 아주 키포인트!)
this 바인딩
식별자와 값을 연결하는 과정 (묶다)
객체 리터럴에서 메서드 내부의 this
는 메서드를 호출한 circle을 가리킨다.
const circle = {
radius: 5,
getDiameter(){
return 2 * this.radius; // this : 메서드를 호출한 객체 = circle
}
}
console.log(circle.getDiameter());
생성자 함수 내부의 this
는 생성자 함수가 생성할 인스턴스를 가리킨다.
function Circle() {
this.radius: radius;
}
Circle.prototype.getDiameter = function(){
return 2 * this.radius; // this : 메서드를 호출한 객체 = circle
}
const circle = new Circle(5);
console.log(circle.getDiameter());
strict mode
에서의 this
위의 예제를 통해, 자바스크립트의 this
는 함수가 호출되는 방식에 따라 바인딩될 값이 동적으로 결정되는 것을 확인했다. 이때 window
가 바인딩되는 경우는 strict mode
에서 undefined
가 바인딩 된다.
this
사용this
사용 : 메서드 호출한 객체 바인딩this
사용 : 생성할 인스턴스 바인딩this
바인딩(반복하는 만큼 중요한) this
바인딩은 함수 호출 방식에 의해 동적으로 결정된다.
이때 함수가 정의될 때 상위 스코프를 결정하는 렉시컬 스코프와 this
가 바인딩되는 시점은 다르다.
this
는 함수가 정의될 때가 아닌 호출될 때 결정되기 때문 !
함수를 호출하는데는 총 4가지의 방법이 있다. 하나씩 살펴보자!
this
에는 전역 객체(window)가 바인딩 됨전역함수도, 중첩함수도, 아무튼 일반 함수로 호출하면 함수 내부의 this
에는 전역 객체가 바인딩된다.
바인딩된 전역 객체는 객체의 프로퍼티/메서드를 참조하기 위한 자기 참조 변수로서 의미가 없다.
strict mode가 적용된 일반 함수에서는 undefined
가 바인딩 된다.
메서드 내에서 정의된 중첩 함수
여도, 일반 함수로 호출되면 역시 this
에는 전역 객체가 바인딩된다.
콜백 함수
도 일반 함수로 호출되면 역시 this
에는 전역 객체가 바인딩된다.
즉, 어떤 함수라도 일반 함수로 호출되면 ! this
에는 전역 객체가 바인딩된다.
this
활용법메서드 내부나 중첩 함수, 콜백 함수에서는 자기 자신을 참조해야 할 때가 자주 찾아온다.
이때 this
바인딩을 메서드의 this
바인딩과 일치하기 위해선 this
를 변수로 할당하여 사용할 수 있다.
var value = 1;
const obj = {
value: 100,
foo(){
const that = this; // obj로 바인딩된 `this`를 변수로 할당
setTimeout(function(){
console.log(that.value); // 100
}, 100}
}
}
obj.foo()
위의 변수를 할당하는 방법 외에도, Function.prototype.apply/call/bind
메서드를 활용할 수 있다.
하지만 요건 조금 이따가 ! 살펴보자 ~ (밀당쓰)
추가로 화살표 함수
를 사용하는 방법도 존재한다. 하지만 이것은 26장에서 살펴볼 예정이다 ! (밀기만 하는중)
this
에는 메서드를 호출한 객체가 바인딩메서드 내부의 this
는 메서드를 소유한 객체가 아닌, 메서드를 호출한 객체에 바인딩 된다.
메서드는 다른 객체의 프로퍼티에 할당하여, 다른 객체의 메서드가 될 수도 있고
메서드는 일반 변수에 할당하여, 일반 함수로 호출될 수도 있다.
const person = {
name: 'sozzang',
getName(){ return this.name }
}
const anotherPerson(){ name: 'kim' }
// getName 메서드를 anotherPerson 객체의 메서드로 할당
anotherPerson.getName = person.getName
console.log(person.getName) // sozzang
console.log(anotherPerson.getName) // kim
const getName = person.getName;
console.log(getName()) // '' : 일반 함수로 호출되어 window.name 값 반환됨
결국 메서드 내부의 this
는 프로퍼티로 메서드를 가리키고 있는 객체와 관계 없이! 메서드를 호출한 객체에 바인딩된다.
this
바인딩 값은?프로토타입 메서드 내부에서 사용된 this
도 일반 메서드와 동일하다. 해당 메서드를 호출한 객체에 바인딩 된다.
Person.prototype
도 객체이므로, 직접 메서드 호출이 가능하다.
function Person(name){ this.name = name; }
Person.prototype.name = 'kim'
Person.prototype.getName = function(){
return this.name;
}
const me = new Person('sozzang')
console.log(me.getName) // sozzang
console.log(Person.prototype.getName()) // kim
생성자 함수 내부의 this
에는 생성자 함수가 생성할 인스턴스가 바인딩된다.
Function.prototype.apply/call/bind
메서드에 의한 간접 호출apply
, call
, bind
메서드는 Function.prototype
의 메서드다.
이 메서드는 함수의 프로토타입에 바인딩되어 있기 때문에 모든 함수가 상속받아 사용이 가능하다.
apply
: 인수로 전달한 배열이 this에 바인딩되어 함수 실행call
: 인수로 전달한 인수 리스트가 this에 바인딩되어 함수 실행bind
: 인수로 전달한 인수 리스트가 this에 바인딩되어 함수 본문 전달apply
, call
메서드Function.prototype.apply(thisArg, [arg1, arg2])
: 배열을 전달 받음Function.prototype.call(thisArg, arg1, arg2, ...)
: 인수 리스트를 전달 받음apply
와 call
메서드의 본질적인 기능은 함수를 호출하는 것이다.
이때 중요한 차이점은, this
로 사용될 애를 첫번째 인수로 전달한다.
즉 첫번째 인수로 전달한 값이 실행된 함수에서 this
로 사용된다.
apply
와 call
사용 예시 : arguments 유사 배열 객체를 배열로대표적인 용도는 arguments
객체와 같은 유사 배열 객체에 배열 메서드를 사용하기 위해서이다.
function convertArgsToArr(){
console.log(arguments); // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
const arr = Array.prototype.slice.call(arguments)
console.log(arr)
return arr;
}
convertArgsToArr(1, 2, 3) // [1, 2, 3]
// 이런것도 가능하다 ! (문자열에 배열 전용 메서드 사용하기)
'1,2,3'.slice('') // '1,2,3'
Array.prototype.slice.call('123') // (3) ['1', '2', '3']
위의 예시를 보면, 배열 메서드인 slice
를 사용하지 못하는 유사 배열 객체 arguments
에 call
메서드로 간접 호출하여 slice
메서드를 사용했다. 물론 apply
도 가능하다.
bind
메서드bind
는 apply
, call
과 다르게 함수를 호출하지 않는다. 첫번째 인수로 교체된 함수 본문을 반환한다.
따라서 bind
메서드는, 1️⃣ 메서드의 this
와 2️⃣ 메서드 내부의 중첩함수/콜백함수의 this
가 불일치하는 문제를 해결하는데 유용하다.
const person = {
name: 'sozzang',
foo(callback){
console.log(this); // this = person : 메서드 축약형은 해당 객체를 참조함
setTimeout(callback.bind(this), 100)
}
}
person.foo(function(){ console.log(this.name) }) // sozzang
콜백 함수 내부의 this를 외부 함수 foo의 this와 일치시켜야 한다.
위와 같이 foo
함수의 this
를 전달하여 변경된 callback
함수 본문을 호출하면 된다.
내가 보려고 기록하는 정리 !
this
는 함수가 호출되는 대상이 바인딩됨
일반 함수는 호출하는 대상이 전역 객체임 왜냐?
사실 모든 일반 함수는 window.함수()인데 전역 객체가 생략된 것이기 때문 !
그래서 일반 함수로 작성된 함수, 중첩함수, 콜백함수 모~두 this
는 전역 객체, 즉 window가 바인딩됨
메서드로 사용되었다면 this
는 무조건 무언가의 객체를 바인딩
왜냐면 메서드를 사용하려면 무조건 객체.메서드() 해야하니까 ~
근데 이때 화살표 함수를 사용하면 화살표 함수는 this
가 없음
그래서 this
가 뭔지 몰라서 상위 컨텍스트한테 물어봄 "this
가 모야?"
상위 컨텍스트는 전역/함수로만 이루어짐 (블록문/객체는 컨텍스트 환경 아님)
그래서 상위의 함수가 있다면 타고 타고 물어보다가 결국 아무도 안 말해주면 전역 객체 바인딩
this
를 활용하는 방법이 3가지 더 있음 바로 apply
, call
, bind
우선 call
이랑 apply
는 함수를 대신 호출하는거 ! 간접 호출 !
call
, apply
의 첫번째 인수로 전달한 값을 this
로 바인딩해서 함수를 실행함
원하는 애를 this
로 사용하기 딱 좋음
bind
는 첫번째 인수로 전달된 값이 this
로 바인딩되긴 하는데 ..
함수가 호출되진 않음 = 실행되진 않음. 함수 본문이 전달됨 (중요)
그래서 setTimeout에서 사용하기 좋음 굿
오랜만에 2일 연속으로 모던 자스 딥다이브를 성공했다 !
아무래도 수업 시간에 this에 대해 많이 배우고 사용해봐서 빠르게 정리할 수 있었던 것 같다.
(하지만 화살표 함수가 상위 컨텍스트의 this를 받아온다는 퀴즈는 틀렸죠?)
낼부터 휴강 + 설 연휴다 ... 조금만 놀고 열심히 공부해야쥐 ~
설 연휴때도 공부하는 멋진 나.. 가보자고 !