this 키워드
this
keyword/variable: 모든 execution context마다(모든 함수마다) 생기는, “owner” of the function을 가리키는 특별 변수
- NOT Static, 함수가 실제로 호출될 때 할당된다.
this
의 예시
- Method의 this
- <Object that is calling the method>
- Simple function call의 this
undefined
(in strict mode, otherwise window
(in the browser)
- Arrow functions의 this
- <
this
of surrounding function (lexical this
)>
- 자기 자신의 this를 갖지는 않는다.
- Event listener의 this
- <DOM element that the handler is attached to>
this
를 브라우저에서 출력하면(console.log(this);) window
가 출력된다.
new
, call
, apply
, bind
- NOT point to the function itself
- NOT the its variable environment
Method?
const jonas = {
name: 'jonas',
year: 1989,
calcAge: function() {
return 2037 - this.year
}
};
jonas.calcAge()
const calcAge = function (birthYear) {
console.log(2037 - birthYear);
console.log(this);
}
calcAge(1991);
const calcAgeArrow = birthYear => {
console.log(2037 - birthYear);
console.log(this);
}
calcAgeArrow(1980);
- 위 함수는
undefined
, 아래 함수는 window
가 출력된다.
const jonas = {
year: 1991,
calcAge: function () {
console.log(this);
}
}
jonas.calcAge();
const jonas = {
year: 1991,
calcAge: function () {
console.log(this);
}
};
jonas.calcAge(); // jonas
const matilda = {
year: 2017,
};
matilda.calcAge = jonas.calcAge;
matilda.calcAge(); // matilda
const f = jonas.calcAge
f(); // undefined
this
키워드는 동적으로 할당 되는 것을 염두해두자.
일반 함수(Regular Function) vs. 화살표 함수(Arrow Function)
method로 화살표 함수를 사용할 경우 주의할 점
const jonas = {
firstName: 'jonas',
year: 1989,
calcAge: function () {
return 2037 - this.year
},
greet: () => console.log(`Hey ${this.firstName}`),
};
jonas.greet(); // Hey undefined
- 화살표 함수(Arrow function)의 this는 부모의 this를 따르는데, 이 함수의 부모는 global scope이다. global scope의 this는
window
이고, window에는 firstName이 정의되지 않았으므로 undefined
가 출력된다.
- 에러가 나지 않고 undefined가 출력되는 것 기억해놓기…🥹
- 그래서 global scope에
var firstName
을 선언해놓았으면 해당 변수의 값이 출력된다.
- 이러한 상황을 막기 위해서 애초에 method에는 화살표 함수 쓰지 말고 function expression을 쓸 것…!
method 안에서 일반 함수를 쓸 때 주의할 점
const jonas = {
firstName: 'jonas',
year: 1989,
calcAge: function () {
console.log(this);
console.log(2037 - this.year);
const isMillenial = function () {
console.log(this.year >= 1981 && this.year <= 1996);
};
isMillenial();
},
greet: () => console.log(`Hey ${this.firstName}`),
};
jonas.calcAge();
- 이 상황에서는 TypeError가 나게 된다.
isMillenial
은 일반 함수이기 때문에 this
가 undefined
이고, undefined
에는 year 프로퍼티가 없기 때문이다.
const jonas = {
firstName: 'jonas',
year: 1989,
calcAge: function () {
console.log(this);
console.log(2037 - this.year);
const self = this; // self or that
const isMillenial = function () {
console.log(self.year >= 1981 && self.year <= 1996);
};
isMillenial();
},
greet: () => console.log(`Hey ${this.firstName}`),
};
- 위 상황의 경우 일반적으로 스코프 체인을 이용,
jonas
object를 가리키는 this
를 새로운 변수(self
)에 저장해서 사용했다.
- 하지만 ES6에서는 화살표 함수를 사용해서 해결할 수 있다.
const jonas = {
firstName: 'jonas',
year: 1989,
calcAge: function () {
console.log(this);
console.log(2037 - this.year);
// const self = this; // self or that
// const isMillenial = function () {
// console.log(self.year >= 1981 && self.year <= 1996);
// };
const isMillenial = () => {
console.log(this); // jonas object
console.log(self.year >= 1981 && self.year <= 1996);
};
isMillenial();
},
greet: () => console.log(`Hey ${this.firstName}`),
};
- 화살표 함수의
this
는 부모(jonas)의 this
를 가리키므로 jonas
object이기 때문이다.
arguments
const addExpr = function (a, b) {
console.log(arguments);
return a + b;
}
addExpr(2, 5); // Arguments(2)
const v = addExpr(2, 5, 8, 12); // Arguments(4)
console.log(v); // 7
- arguments에는 배열로 인자 값들이 들어가있다.
- 정해진 인자 개수를 넘으면 에러가 나야할 것 같지만 잘 실행된다. 🥹
var addArrow = (a, b) => {
console.log(arguments);
return a + b;
}
addArrow(2, 5); // ReferenceError
- 화살표 함수에는 arguments를 사용할 수 없다.
Primitives vs. Objects(Reference Types)
- Primitives: Number, String, Boolean, Undefined, Null, Symbol, Bigint = 원시 타입
- 콜 스택에 실제 데이터 값이 저장된다.
- 같은 값을 가질 경우 같은 주소값을 갖는다.
- 변수에 변수를 할당할 경우 같은 주소값을 갖는다.(당연)
- Objects: object literal, Arrays, Functions 등… = 참조 타입
- 힙에 실제 데이터 값이 저장된다.
- 콜 스택 메모리 주소에 저장된 값은 힙의 메모리 주소이다.
- 같은 값을 갖더라도 다른 주소에 저장된다.
- 변수에 변수를 할당할 경우 같은 주소값을 갖는다.(당연)
let age = 30;
let oldAge = age;
age = 31;
console.log(age); // 31
console.log(oldAge); // 30
const me = {
name: 'Jonas',
age: 30,
};
const friend = me;
friend.age = 27;
console.log(me.age); // 27
console.log(friend.age); // 27
- 원시 타입인 age는 값을 변경하면 새로운 주소에 새로운 값이 저장되고, 새로운 메모리 주소를 갖게 된다. oldAge는 아직 이전 메모리 주소에 저장된 값을 가리키고 있으므로 값이 그대로이다.
- 참조 타입인 me는 값이 변경되면 주소는 그대로고 값만 바뀐다. friend는 me 주소의 값을 똑같이 바라보고 있으므로 바뀐 값이 출력된다.
const
의 값이 immutable 하다는 것은 원시 타입일 때 해당되고, 참조 타입일 경우 데이터를 추가하거나 삭제하거나 하는 등의 변경이 가능하다.
- 하지만 const에 새로운 객체를 할당하는 등의 변경은 불가능하다.(e.g.
const me = {};
는 TypeError 발생)
Object.assign
()
const meCopy = Object.assign({}, me);
meCopy.age = 30
console.log(me.age); // 27
console.log(meCopy.age); // 30
- 새로운 객체로 할당하기 위해서는
Object.assign
() 함수를 사용하면 된다.
- 하지만 중첩된 object의 경우 여전히 같은 메모리를 가리키고 있다. (=shallow copy)
- deep copy를 하려면 Lo-dash 같은 외부 라이브러리를 사용하기도 한다. 이건 다음에 공부…
- c.f. 파이썬에서도 list, set 등은 참조 변수로 작동하기 때문에 copy 혹은 deepcopy함수를 사용해야 하는 경우가 있다. (
is
, id()
로 같은 객체를 가리키는 것을 확인할 수 있음)
Related