this
의 값은 실행 컨텍스트가 생성될 때(대부분의 경우 함수를 호출할 때) 정해진다.
const obj1 = {
callThis: function() { return this; }
}
const obj2 = {
callThis: this
}
console.log(obj1.callThis()); // obj1
console.log(obj2.callThis); // window
// 함수 console.log가 호출될 때 this의 값은 window
객체 안에 있는 함수, 즉 메서드라도 객체의 메서드로 호출하지 않는 경우에는(일반 함수로 호출) this
값이 전역이다.
const obj = {
outer: function () {
console.log(this);
const inner = function () {
console.log(this);
};
inner(); // this : Window
}
};
obj.outer(); // this : obj
call()
을 이용하여 this
(아래 예시에서는 obj) 값을 지정하고 호출하면 this
값은 지정한 값이다.
const obj = {
outer: function () {
console.log(this);
const inner = function () {
console.log(this);
};
inner.call(this); // this : obj
}
};
obj.outer(); // this : obj
bind()
역시 이용할 수 있다.
const obj = {
outer: function () {
console.log(this);
const inner = function () {
console.log(this);
}.bind(this);
inner(); // this : obj
}
};
obj.outer(); // this : obj
화살표 함수는 실행 컨텍스트를 생성할 때 this
값이 무엇인지 정하는 - this
값을 바인딩하는 과정이 없어서, 실행 시 자바스크립트 엔진이 스코프 체인 상에서 찾을 수 있는 가장 가까운 this
값을 사용한다.
const obj = {
outer: function () {
console.log(this);
const inner = () => {
console.log(this);
};
inner(); // this : obj
}
};
obj.outer(); // this : obj
[ WindowTimers.setTimeout() this 문제 | MDN ]
'...setTimeout()에 의해 실행된 코드는 별도의 실행 컨텍스트에서 setTimeout이 호출된 함수로 호출됩니다. 호출된 함수에 대해서는 this 키워드를 설정하는 일반적인 규칙이 적용되며, this를 설정 혹은 할당하지 않은 경우, non-strict 모드에서 전역(혹은 window) 객체, strict모드에서 undefined를 기본 값으로 합니다. ..'
setTimeout()
은 기본 this
값이 전역이다. 원하는 this
값을 지정하려면 함수를 함수로 감싸거나, bind()
를 이용한다.
const obj1 = {
name: 'obj1',
func: function () {
console.log(this.name);
}
}
const obj2 = {
name: 'obj2',
func: obj1.func
}
// 문제 - this값이 전역
setTimeout(obj1.func, 100); // this: window
setTimeout(obj2.func, 200);
// 해결책 1 Wrapper Function
setTimeout(() => {obj1.func()}, 300); // this: obj1
setTimeout(() => {obj2.func()}, 400);
// 해결책 2 bind()
setTimeout(obj1.func.bind(obj1), 500); // this: obj1
setTimeout(obj2.func.bind(obj2), 600);
여러 배열 프로토타입 메서드는 this
값을 지정할 수 있다. Array.prototype.map()
을 this
값을 지정하여 이용해 보았다.
const obj = {
a: 10
}
const arr = [1, 2, 3, 4, 5];
const result = arr.map(function (val) {
return val + this.a
}, obj); // callback의 다음 인자로 this값을 전달한다
console.log(result);
// [11, 12, 13, 14, 15]
하지만 이때 callback
을 화살표 함수로 표기하면 this
값을 전달해도 인식하지 않는다.
const obj = {
a: 10
}
const arr = [1, 2, 3, 4, 5];
// 화살표 함수를 쓸 경우 this를 obj로 인식하지 않고 전역으로 인식한다
// this(window)에 a라는 변수가 없으므로 undiefined
const result = arr.map(val => val + this.a, obj);
console.log(result);
// [NaN, NaN, NaN, NaN, NaN]
[ Array-Like Objects and Generic Methods | Dr. Axel Rauschmayer ]
key
가 0 또는 양의 정수인 프로퍼티가 존재하고 length
프로퍼티의 값이 0 또는 양의 정수인 객체를 유사 배열이라고 한다.
const obj1 = {
0: 10,
1: 20,
2: 30,
length: 3
}
Array.prototype.push.call(obj1, 40); // call()로 배열의 프로토타입 메서드 push를 사용
console.log(obj1); // 값이 배열과 비슷하게 변한다
// {0: 10, 1: 20, 2: 30, 3: 40, length: 4}
유사 배열을 배열로 바꾸는 대표적인 방법은 아래와 같다.
1. call()로 배열의 프로토타입 메서드 slice()를 사용하기
2. Array.from() 사용하기
// nodeList는 유사 배열
const nodeList = document.querySelectorAll('div');
const nodeArr = Array.prototype.slice.call(nodeList); // 1번
// const nodeArr = Array.from(nodeList); // 2번
console.log(nodeArr);
// 1~2번 모두 같은 결과를 가져온다
// [div, div, div]
[ Overview of Events and Handlers | MDN ]
[ 이벤트 참조 | MDN ]
addEventListener()
는 callback
을 호출할 때 첫 번째 인자로 '이벤트 객체'를 전달한다.
이벤트 객체란, 특정 타입의 이벤트와 관련이 있는 객체로 type, target 등 여러 속성을 가지고 있다. 우선 속성을 자세히 들여다보기 위해 색이름을 텍스트로 표시한 리스트 엘리먼트를 만들고, 텍스트를 클릭했을 때 이벤트 객체가 무엇인지 확인하는 코드를 작성했다.
const colors = ['red', 'orange', 'yellow', 'green', 'blue'];
const ul = document.createElement('ul');
colors.forEach(function(color) {
const li = document.createElement('li');
li.textContent = color;
li.addEventListener('click', function(event) {
console.log(event.type, event.target, event.target.value);
});
ul.appendChild(li);
})
document.body.appendChild(ul);
event.type : click // 이벤트 이름은 무엇인가?
event.target : <li>red</li> // 어디서 이벤트가 발생했는가?
event.target.value : 0 // 이벤트가 발생한 곳의 value값은 무엇인가?
위 코드를, 텍스트를 클릭했을 때 'your choice is (색이름)'라는 문장이 콘솔창에 뜨는 코드로 변경했다. 이때 색이름을 콘솔창에 띄우는 함수 noticeChoice
를 forEach()
메서드의 바깥에 따로 작성하고 callback
으로 사용하면, 인자로 이벤트 객체가 전달되며 'your choice is [object MouseEvent]'가 출력된다.
const colors = ['red', 'orange', 'yellow', 'green', 'blue'];
const ul = document.createElement('ul');
const noticeChoice1 = function(color) {
console.log(`your choice is ${color}`);
}
colors.forEach(function (color) {
const li = document.createElement('li');
li.textContent = color;
li.addEventListener('click', noticeChoice);
ul.appendChild(li);
})
document.body.appendChild(ul);
// your choice is [object MouseEvent]
코드를 어떻게 바꾸면 'your choic is red'가 출력될까? 함수 noticeChoice
에 내부함수를 만들고, 내부함수에 원하는 코드를 넣어 클로저로 이용하면 해결할 수 있다.
변경한 코드는 사용자가 엘리먼트를 클릭 시 아래 순서로 동작한다.
1. 이벤트 객체가 noticeChoice(color)
의 첫번째 인자로 전달된다.
2. addEventListener()
가 noticeChoice(color)(이벤트 객체)
를 실행한다.
3. noticeChoice의 내부함수에서 전달된 이벤트 객체를 받지만, 인자를 이용하는 코드가 없기에 별도 동작도 없다.
4. 외부함수에 전달된 인자인 color를 내부 함수가 이용해 'your choice is ${color}'를 출력한다.
const colors = ['red', 'orange', 'yellow', 'green', 'blue'];
const ul = document.createElement('ul');
const noticeChoice = function (color) {
return function () {
console.log(`your choice is ${color}`);
}
}
colors.forEach(function (color) {
const li = document.createElement('li');
li.textContent = color;
li.addEventListener('click', noticeChoice(color));
ul.appendChild(li);
})
document.body.appendChild(ul);
your choice is red
your choice is orange
your choice is yellow
your choice is green
your choice is blue