728x90
[3장] this
❓this
대부분의 객체지향언어에서 - 클래스로 생성한 인스턴스 객체
JS에서 - 어디서든 사용 가능 (상황에 따라 this가 바라보는 대상이 달라짐)
→ 함수와 객체(메서드)의 구분이 느슨한 자바스크립트에서 this는 실질적으로 이 둘을 구분하는 거의 유일한 기능임!
1. 상황에 따라 달라지는 this
- this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다.
- 실행 컨텍스트는 함수를 호출할 때 생성함 = this는 함수를 호출할 때 결정된다.
📍전역 공간에서의 this
- 전역공간의 this = 전역객체
- 브라우저에서의 전역객체 = window
- Node.js에서의 전역객체 = global
var a = 1
console.log(a) // 1
console.log(window.a) // 1 (브라우저 환경에서)
console.log(this.a) // 1 (전역 스코프에서, 브라우저 환경에서는 window.a와 동일)
→ 전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다.
var a = 1
delete window.a // false
var b = 2
delete b // false
window.c = 3
delete window.c // true
window.d = 4
delete d // true
- 처음부터 전역객체의 프로퍼티로 할당한 경우 - 삭제 O
- 전역변수로 선언한 경우 - 삭제X
- 사용자가 의도치 않게 삭제하는 것을 방지하는 차원..
📍함수, 메서드로서 호출할 때 그 함수, 메서드 내부에서의 this
👊🏻함수 vs. 메서드
- 함수 - 독립성 O
- 메서드 - 독립성X
var a = 10
// 함수 정의
function greet() {
console.log('이 함수의 this는 전역 객체를 가리킵니다.')
console.log('전역 객체의 속성에 접근할 수 있습니다.')
console.log('this.a:', this.a) // this.a: 10
}
// 메서드 정의
var obj = {
greet: function () {
console.log('이 메서드의 this는 객체를 가리킵니다.')
console.log('객체의 속성에 접근할 수 있습니다.')
console.log('this.a:', this.a) // this.a: undefined
},
}
// 함수 호출
greet()
// 메서드 호출
obj.greet()
🤔메서드 내부 함수에서의 this를 우회하는 방법
외부 변수에 this
를 할당한 다음, 내부 함수에서 그 변수를 참조하는 것
var obj = {
name: '보리',
greet: function () {
var self = this // 외부 변수에 this 할당
function innerFunction() {
console.log('안녕하세요, ' + self.name + '님!')
}
innerFunction()
},
}
obj.greet() // 안녕하세요, 보리님!
🤔this를 바인딩하지 않는 함수
- 함수가 호출될 때
this
컨텍스트가 바인딩되지 않도록 하려면 일반적으로 화살표 함수(arrow function)를 사용한다. - 화살표 함수는 함수가 정의된 시점에서 상위 스코프의
this
를 캡처하기 때문에 함수가 호출될 때this
가 변경되지 않는다.
var obj = {
name: '보리',
greet: function () {
setTimeout(() => {
console.log('안녕하세요, ' + this.name + '님!')
}, 1000)
},
}
obj.greet() // (1초 후) "안녕하세요, 보리님!
- 콜백 함수 내부의
this
는obj
객체를 가리킨다. - 만약 화살표 함수 대신 일반 함수를 사용했다면,
setTimeout
내에서this
는window
객체를 가리키게 될 것임…
📍생성자 함수 내부에서의 this
- 생성자 함수 내부에서
this
는 새로 생성된 객체를 가리킨다. - 생성자 함수는
new
키워드를 사용하여 호출되며, 이때this
는 새로 생성된 객체에 바인딩된다.
function Person(name, age) {
// 새로운 객체에 속성 할당
this.name = name
this.age = age
// 메서드 정의
this.greet = function () {
console.log(
'안녕하세요, 제 이름은 ' +
this.name +
'이고, 나이는 ' +
this.age +
'살입니다.'
)
}
}
// 생성자 함수를 사용하여 객체 생성
var person1 = new Person('보리', 30)
var person2 = new Person('보라', 25)
// 객체의 메서드 호출
person1.greet() // "안녕하세요, 제 이름은 보리이고, 나이는 30살입니다."
person2.greet() // "안녕하세요, 제 이름은 보라이고, 나이는 25살입니다."
2. 명시적으로 this를 바인딩하는 방법
📍call
- 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령
- 첫 번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 한다.
var person = {
name: '보리',
greet: function () {
console.log('안녕하세요, ' + this.name + '님!')
},
}
var anotherPerson = {
name: '모리',
}
person.greet() // "안녕하세요, 보리님!"
person.greet.call(anotherPerson) // "안녕하세요, 모리님!"
call
메서드를 사용하여 person.greet
함수를 호출할 때 this
를 anotherPerson
객체로 설정
📍apply
- call 메서드와 기능적을 완전히 동일
- 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정함
var person = {
name: '보리',
greet: function (greeting) {
console.log(greeting + ', ' + this.name + '님!')
},
}
var anotherPerson = {
name: '모리',
}
person.greet('안녕하세요') // "안녕하세요, 보리님!"
person.greet.apply(anotherPerson, ['안녕하세요']) // "안녕하세요, 모리님!"
💡유사배열객체에 배열 메서드 적용
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
}
// obj에 push 메서드 호출하여 요소 추가
Array.prototype.push.call(obj, 'd')
console.log(obj) // {0: "a", 1: "b", 2: "c", 3: "d", length: 4}
// obj를 배열로 변환하여 arr에 저장
var arr = Array.prototype.slice.call(obj)
console.log(arr) // ["a", "b", "c", "d"]
💡Array.from()
- 유사 배열 객체나 이터러블 객체를 배열로 변환하는 메서드
- 제공된 객체나 값으로 새로운 Array 객체를 만든다.
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
}
var arr = Array.from(obj)
console.log(arr) // ["a", "b", "c"]
💡생성자 내부에서 다른 생성자 호출
function Animal(type) {
this.type = type
}
function Person(name, age, type) {
// Person 생성자 함수 내에서 Animal 생성자 함수 호출
// Animal 생성자 함수가 현재 Person 생성자 함수의 인스턴스인 this 컨텍스트로 호출
// Person 인스턴스에 type 속성이 추가되어 초기화
Animal.call(this, type)
this.name = name
this.age = age
}
var bori = new Person('bori', 30, 'Dog')
console.log(bori) // Person { type: 'Dog', name: 'bori', age: 30 }
📍bind
- call과 비슷하지만 즉시 호출하지는 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드
- 새로운 함수를 호출할 때 인수를 넘기면 그 인수들은 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록된다.
var person = {
name: '보리',
greet: function (greeting) {
console.log(greeting + ', ' + this.name + '님!')
},
}
var anotherPerson = {
name: '모리',
}
var greetAnotherPerson = person.greet.bind(anotherPerson, '안녕하세요')
// greetAnotherPerson 함수를 호출할 때 "안녕하세요"라는 인수가 이미 고정
greetAnotherPerson() // "안녕하세요, 모리님!"
💡상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기
function outerFunction() {
// 상위 컨텍스트의 this
console.log(this) // window { ... }
function innerFunction() {
// bind 메서드로 전달된 this
console.log(this) // window { ... }
}
innerFunction.bind(this)()
}
outerFunction()
📍화살표 함수의 예외사항
function outerFunction() {
// // 상위 컨텍스트의 this
console.log(this) // window { ... }
var innerFunction = () => {
// 상위 컨텍스트의 this를 캡처
console.log(this) // window { ... }
}
innerFunction()
}
outerFunction()
- 화살표 함수 내부에는 this가 아예 없으며 접근하고자 하면 스코프체인상 가장 가까운 this에 접근하게 된다.
📍별도의 인자로 this를 받는 경우 (콜백 함수 내에서의 this)
- 콜백 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체를 인자로 지정할 수 있는 경우가 있다.
- 내부 요소에 대해 같은 동작을 반복 수행해야 하는 배열 메서드에 많이 포진돼있음.
- set, map, forEach 등…
var obj = {
name: '보리',
print: function () {
var arr = [1, 2, 3]
arr.forEach(function (element) {
console.log(this.name + '의 번호:', element)
}, this) // 두 번째 인자로 this를 전달
},
}
obj.print()
// 출력 결과
// 보리의 번호: 1
// 보리의 번호: 2
// 보리의 번호: 3
✨정리
암묵적 this 바인딩
- 전역 공간에서의 this는 전역 객체를 참조한다. (브라우저 - window, Node.js - global)
- 어떤 함수를 메서드로서 호출한 경우 this는 호출 주체(메서드명 앞의 객체)를 참조한다.
- 어떤 함수를 함수로서 호출한 경우 this는 전역 객체를 참조한다.
- 콜백 함수 내부에서의 this는 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며, 정의하지 않은 경우에는 전역 객체 참조한다.
- 생성자 함수에서의 this는 생성될 인스턴스를 참조합니다.
명시적 this 바인딩
- call, apply 메서드는 this를 명시적으로 지정하면서 함수 또는 메서드를 호출한다.
- bind 메서드는 this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만든다.
- 요소를 순회하면서 콜백 함수를 반복 호출하는 내용의 일부 메서드는 별도의 인자로 this를 받기도 한다.
'📖 책 찢기 > 코어 자바스크립트' 카테고리의 다른 글
[코어자바스크립트] 6장. 프로토타입 (0) | 2024.06.18 |
---|---|
[코어자바스크립트] 5장. 클로저 (0) | 2024.06.17 |
[코어자바스크립트] 4장. 콜백 함수 (0) | 2024.06.14 |
[코어 자바스크립트] 2장. 실행 컨텍스트 (3) | 2024.06.11 |
[코어 자바스크립트] 1장. 데이터 타입 (0) | 2024.06.10 |