JavaScript
[JS] this 에 대한 모든 것
Leo(상원)
2022. 12. 1. 12:10
반응형
1. 상황에 따라 달라지는 this
this는 실행 컨텍스트가 생성될 때 결정(this binding : 묶는 것) === this는 함수를 호출할 때 결정
i. 전역 공간에서의 this
- 1. 전역 공간에서 this는 전역 객체를 가르킴
- window(브라우저 환경), global(node 환경)
console.log(this) // 전역공간에서의 this는 window를 지칭한다.
console.log(window)
console.log(this === window) // true
메서드로서 호출할 때 그 메서드 내부에서의 this
i. 함수 vs 메서드
- 기준: 독립성
- 함수: 그자체로 독립적인 기능을 수행
- 메서드: 자신을 호출한 대상 객체에 관한 동작을 수행
ii. 함수와 메서드가 호출될 때, this는 각각 다르게 할당
var func = function (x) {
console.log(this, x)
}
var obj = {
method: func,
}
obj.method(2) // { method: f } 2
iii. 함수로서의 호출과 메서드로서의 호출 구분 기준: . []
var obj = {
method: function (x) { console.log(this, x) }
}
obj.method(1) // { method: f } 1
obj['method'](2) // { method: f } 2
iv. 메서드 내부에서의 this
var obj = {
methodA: function () { console.log(this) },
inner: {
methodB: function() { console.log(this) },
}
}
obj.methodA() // this === obj
obj["methodA"]() // this === obj
obj.inner.methodB() // this === obj.inner
obj.inner["methodB"]() // this === obj.inner
obj["inner"].methodB() // this === obj.inner
obj["inner"]["methodB"]() // this === obj.inner
함수로서 호출할 때 그 함수 내부에서의 this
i. 함수 내부에서의 this
- 어떤 함수를 함수로서 호출할 경우, this는 지정되지 않음(호출 주제가 없으므로)
- 실행컨텍스트를 활성화할 당시 this가 지정되지 않은 경우, this는 전역 객체를 바라봄
- 따라서, 함수로서 "독립적으로" 호출할 때는 this는 전역 객체
ii. 메서드의 내부함수에서의 this
- 메서드의 내부라고 해도, 함수로서 호출한다면 this는 전역 객체
var obj1 = {
outer: function() {
console.log(this) // obj1
var innerFunc = function() {
console.log(this) // window
}
innerFunc() // 함수로서의 call
var obj2 = {
innerMethod: innerFunc
}
obj2.innerMethod()
}
}
obj1.outer()
- this 바인딩에 관해서는 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지)은 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지가 관건임!
iii. 메서드의 내부 함수에서의 this 우회
변수 활용
var obj1 = {
outer: function() {
console.log(this) // this === obj1
var innerFunc1 = function() {
console.log(this) // this === window
}
innerFunc1()
var self = this
var innerFunc2 = function() {
console.log(self)
}
innerFunc2()
}
}
obj1.outer()
화살표 함수
- ES6에서 처음 도입된 화살표 함수는, 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 없음 (따라서, this는 이전의 값-상위값-이 유지됨)
var obj = {
outer: function() {
console.log(this)
var innerFunc = () => {
console.log(this)
}
innerFunc()
}
}
obj.outer()
콜백 함수 호출 시 그 함수 내부에서의 this
setTimeout (function () { console.log(this) }, 300) // window
[1, 2, 3, 4, 5].forEach(function(x) { console.log(this, x)}) // window
document.body.innerHTML += `<button id="a">클릭</button>`
document.body.querySelector("#a").addEventListener("click", function(e) {
console.log(this, e) // button tag
})
- setTimeout 함수, forEach 메서드는 콜백 함수를 호출할 때 대상이 될 this 를 지정하지 않으므로, this는 곧 window 객체
- addEventListner 메서드는 콜백 함수 호출 시, 자신의 this를 상속하므로, this는 addEventListner의 앞부분 (button 태그)
생성자 함수 내부에서의 this
i. 생성자: 구체적인 인스턴스를 만들기 위한 일종의 툴
ii. 공통 속성들이 이미 준비돼 있음
var Cat = function (name, age) {
this.bark = "야옹"
this.name = name
this.age = age
}
var choco = new Cat("초코", 7) // this : choco
var nabi = new Cat("나비", 5) // this : nabi
명시적 this 바인딩 (자동으로 부여되는 상황별 this의 규칙을 깨고 this에 별도의 값을 저장하는 방법)
i. call 메서드
- 호출 주체인 함수를 즉시 실행하는 명령어
예시)
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
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
ii. apply 메서드
- call 메서드와 완정 동일
- 두 번째 인자가 배열인 부분만 다름
예시)
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
iii. call / apply 메서드 활용
- 유사배열객체 (array-like-object)에 배열 메서드를 적용
객체에는 배열 메서드를 직접 적용할 수 없어요.
유사배열객체에는 call 또는 apply 메서드를 이용해 배열 메서드를 차용할 수 있어요.
var obj = {
0: "a",
1: "b",
2: "c",
length: 3
}
Array.prototype.push.call(obj, "d")
console.log(obj) // { 0: "a", 1: "b", 2: "c", 3: "d", length: 4 }
var arr = Array.prototype.slice.call(obj)
console.log(arr) // [ "a", "b", "c", "d" ]
- arguments, NodeList에 배열 메서드를 적용 (VScode)
- Array.from 메서드(ES6)
var obj = {
0: "a",
1: "b",
2: "c",
length: 3
}
var arr = Array.from(obj)
console.log(arr)
- 생성자 내부에서 다른 생성자를 호출 (공통된 내용의 반복 제거)
function Person(name, gender) {
this.name = name
this.gender = gender
}
function Student(name, gender, school) {
Person.call(this, name, gender)
this.school = school
}
function Employee(name, gender, company) {
Person.apply(this, [name, gender])
this.company = company
}
var sw = new Student("상원", "male", "서울대")
var leo = new Employee("레오", "female", "애플")
- 여러 인수를 묶어 하나의 배열로 전달할 때 apply 사용
1. 최대 / 최솟값 예제
// 비효율
var numbers = [10, 20, 3, 16, 45]
var max = min = numbers[0]
numbers.forEach(function(number) {
if (number > max) {
max = number
}
if (number < min) {
min = number
}
})
console.log(max, min)
// 효율
var numbers = [10, 20, 3, 16, 45]
var max = Math.max.apply(null, numbers)
var min = Math.min.apply(null, numbers)
console.log(max, min)
// Spread Operation(ES6)
const numbers = [10, 20, 3, 16, 45]
const max = Math.max(...numbers)
const min = Math.min(...numbers)
console.log(max, min)
bind 메서드
i. call과 비슷하지만, 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하는 메서드
ii. 목적
- 함수에 this를 미리 적용하는 것
- 부분 적용 함수 구현
var func = function (a, b, c, d) {
console.log(this, a, ,b, c, d)
}
func(1, 2, 3, 4) // window 객체
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
iii. name 프로퍼티
- bind 메서드를 적용해서 새로 만든 함수는 name 프로퍼티 "bound" 라는 접두어가 붙음 (추적하기가 쉬움)
var func = function (a, b, c, d) {
console.log(this, a, ,b, c ,d)
}
var bindFunc = func.bind({ x : 1 }, 4, 5)
console.log( func.name) // func
console.log(bindFunc.name) // bound func
iv. 상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기
내부함수
- 메서드의 내부함수에서 메서드의 this를 그대로 사용하기 위한 방법
- self 등의 변수를 활용한 우회법보다 apply, call, bind를 사용하면 깔끔하게 처리 가능
var obj = {
outer: function() {
console.log(this)
var innerFunc = function () {
console.log(this)
}
innerFunc.call(this)
}
}
obj.outer()
var obj = {
outer: function() {
console.log(this)
var innerFunc = function () {
console.log(this)
} .bind(this)
innerFunc()
}
}
obj.outer()
콜백함수
- 콜백함수도 함수이기 때문에, 함수가 인자로 전달될 때는 함수 자체로 전달
- bind메서드를 이용해 this를 입맛에 맞게 변경 가능
var obj = {
logThis: function () {
console.log(this)
},
logThisLater1: function () { // 정상동작 x
setTimeOut(this.logthis, 500)
},
logThisLater2: function () { // 정상동작 o
setTimeOut(this.logThis.bind(this), 1000)
}
}
obj.logThisLater1()
obj.logThisLater2()
화살표 함수의 예외사항
- 화살표 함수는 실행 컨텍스트 생성 시, this를 바인딩하는 과정이 제외
- 이 함수 내부에서 this가 아예 없으며, 접근코자 하면 스코프체인상 가장 가까운 this에 접근하게 됨
- this우회, apply, call, bind보다 편리한 방법
var obj = {
outer: function () {
console.log(this)
var innerFunc = () => {
console.log(this)
}
innerFunc()
}
}
obj.outer()
별도의 인자로 this를 받는 경우 (콜백 함수 내에서의 this)
- 콜백 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체를 인자로 지정 할 수 있음
- 배열과 관련된 메서드에 많이 존재하며, set, map 등의 메서드에도 일부 존재함
// forEach 예시
var report = {
sum : 0,
count : 0,
add : function () {
var args = Array.prototpye.slice.call(arguments)
args.forEach(function (entry) {
this.sum += entry
++this.count
}, this)
},
average: function () {
return this.sum / this.count
}
}
report.add(60, 85, 95)
console.log(report.sum, report.count, report.average()
// 콜백 함수와 함께 thisArg를 인자로 받는 메서드
// forEach, map, filter, some, every, find, findIndex, flatMap, from, forEach(Set, Map)
반응형