개요
- obj.name()형태로 호출한 함수(메소드)에서 this로 인스턴스(오브젝트)를 참조 합니다.
- 실행 콘텍스트의 this 바인딩 컴포넌트에 바인딩됩니다.
this와 글로벌 / window 오브젝트
- 글로벌 오브젝트에서 this는 글로벌 오브젝트를 참조합니다.
- window는 JS에서 만든 것이 아니고, 글로벌 오브젝트의 스코프도 아닙니다. 하지만, window와 글로벌 오브젝트를 같은 선상에서 사용합니다.
- Host 오브젝트 개념을 적용하기 때문입니다.
글로벌 오브젝트에 코드를 작성
⇒ window.onload = function(){...} 밖에 작성
- this가 window 참조
console.log(this === window);
// true
- this로 글로벌 변수 사용
var value = 100;
console.log(this.value);
// 100
- window로 글로벌 변수 사용
var value = 100;
console.log(window.value);
// 100
- this.value = 100; 형태로 값 할당
this.value = 100;
console.log(window.value);
// 100
⇒ window 오브젝트와 같이 다른 오브젝트를 마치 내것 처럼 사용하는 개념이 Host 오브젝트입니다. DOM 오브젝트도 Host 오브젝트입니다.
window 오브젝트에 코드를 작성
⇒ window.onload = function() { // 안에 코드 작성 };
onload 이벤트가 발생하면 실행 컨텍스트를 만들게 되고 this 바인딩 컴포넌트에 window가 할당됩니다.
- this가 window 참조
⇒ onload()가 window의 이벤트 핸들러 함수이기 때문에 앞에 작성한 window를 this로 참조합니다. 따라서, this === window가 true를 반환합니다. window.onload = function() {
console.log(this === window);
};
// true
- this로 로컬(지역)변수 사용
⇒ var value에서 value는 핸들러 함수의 지역변수입니다. 근데 this는 window 오브젝트를 참조하기에 this.value로 지역 변수에 액세스 할 수 없습니다. window.onload = function() {
var value = 100;
console.log(this.value);
};
// undefined
- this.value = 100; 형태로 값 할당
⇒ this가 window 오브젝트를 참조하기에 window 오브젝트에 value가 할당되고 100이 출력가능해집니다. window.onload = function() {
this.value = 100;
console.log(window.value);
};
// 100
⇒ 함수 앞에 작성한 오브젝트를 항상 함수 안의 this로 참조합니다.
This 참조 범위
this와 strict 모드
- 오브젝트.함수이름() 형태로 함수 호출
- 글로벌 오브젝트는 오브젝트 이름이 없기에 함수 이름만 작성하여 호출합니다.
- strict 모드에서는 window.book()처럼 book()앞에 window를 글로벌 오브젝트로 작성합니다.
- 함수 앞에 오브젝트를 작성하지 않으면 this 바인딩 컴포넌트에 undefined가 설정되기에 this로 window를 참조할 수 없습니다.
예시
function book() {
"use strict";
return this;
};
var result = book();
console.log(result);
// undefined
⇒ 호출하는 book()함수 앞에 오브젝트를 작성하지 않으면 return this에서 undefined를 반환합니다. 이는 this 바인딩 컴포넌트에 undefined가 설정된다는 의미입니다.
function book() {
"use strict";
return this;
};
var result = window.book();
console.log(result===window);
// true
⇒ 호출하는 book() 함수 앞에 window 오브젝트를 작성하면, book 함수가 글로벌 함수이므로 호출되며 return this에서 window오브젝트를 반환합니다.
this 참조 오브젝트
var book = {
point: 100,
member: {
point: 200,
get: function() {
console.log(this === book.member);
console.log(this.point);
}
}
};
book.member.get();
// true
// 200
- 마지막 줄에서 book.member.get() 호출
- this가 member 오브젝트를 참조합니다.
- book은 get()을 찾아가서 호출하는 경로 역할
- console.log(this === book.member);
- this가 book.member를 참조하기 때문에 실행결과로 true가 출력됩니다.
- 즉, this 바인딩 컴포넌트에 book.member 오브젝트가 설정됩니다.
- console.log(this.point);
- this가 book.member를 참조하기에 book.point값인 100을 출력하지 않고, book.member의 200을 출력합니다.
예시
var book = {
value: 123,
get: function() {
var value = 456;
console.log(this === window);
console.log(this.value);
}
};
var fn = book.get;
fn();
// true
// undefined
- 마지막 줄에서 fn() 호출
- 작성된 오브젝트가 없기에 this 바인딩 컴포넌트에 window 오브젝트를 설정합니다.
- var fn = book.get;
- book.get을 호출하면서 this는 fn()에 설정된 widnow를 참조합니다.
- var value = 456 은 get 메소드의 지역변수로 할당됩니다.
- console.log(this === window);
- this가 window를 참조하기 때문에 실행결과로 true를 출력합니다.
- console.log(this.value);
- window.value 값은 존재하지 않으므로 undefined를 출력합니다.
function getTitle() {
console.log("HTML책");
};
var book = function() {
function getTitle() {
console.log("JS책");
};
this.getTitle();
getTitle();
};
book();
// HTML책
// JS책
- 마지막 줄에서 book() 호출
- 작성된 오브젝트가 없기에 this 바인딩 컴포넌트에 window 오브젝트를 설정합니다.
- this.getTitle()
- window.getTitle() 와 동일하기 때문에 HTML책을 출력합니다.
- getTitle()
- 현재의 실행 영역에서 선언적 환경 레코드 있는 function getTitle() 를 먼저 호출하면서 JS책 출력합니다.
this와 인스턴스
⇒ 일관된 환경에서 값만 다르게 가져가는 데이터 중심의 처리
관계 생성 과정
var book = {};
book.Point = function(point) {
this.point = point;
};
book.Point.prototype.getPoint = function() {
console.log(this.point);
};
var obj = new book.Point(100);
obj.getPoint();
// 100
- var obj = new book.Point(100);
- this.point = point;
- this가 생성한 인스턴스를 참조하므로 point는 인스턴스 프로퍼티가 됩니다.
- 이 논리로 인스턴스마다 프로퍼티 이름과 값을 유지할 수 있습니다.
- obj.getPoint();
- obj 인스턴스의 getPoint() 메소드를 호출
- console.log(this.point);
- obj.getPoint()로 호출, this가 obj참조
- obj는 book.point의 인스턴스
- 인스턴스의 point값 출력
this와 call() 메소드
getTotal.call(this, 10, 20)
- 10과 20을 파라미터 값으로 넘겨줍니다.
- 첫 번째는 파라미터 값으로 넘어가지 않고 두 번째부터 넘어갑니다.
첫 번째 파라미터에 호출된 함수에서 this로 참조할 오브젝트를 작성 하는데 this이외에 다른 오브젝트도 사용이 가능합니다. this 바인딩 컴포넌트에 해당 오브젝트가 바인딩
this사용
글로벌 오브젝트에서 실행 시
"use strict"
var value = 100;
function get(param) {
return param + this.value;
};
var result = get.call(this, 20);
console.log(result);
// 120
- get.call(this, 20)
- return param + this.value;
- this가 글로벌 오브젝트를 참조하므로 var value = 100을 사용합니다.
- 만일, call()을 사용하지 않고 get(20)으로 호출하면
useStrict 환경에서 get 앞에 작성한 오브젝트가 없기에 this가 undefined를 참조하므로 에러가 발생합니다.
Object 사용
var get = function(value) {
return this.base * this.rate + value;
};
var value = {base: 20, rate: 30};
var result = get.call(value, 50);
console.log(result);
// 650
- var result = get.call(value, 50);
- call()의 첫 번째에 Object를 작성합니다.
- 50은 파라미터 값입니다.
- return this.base * this.rate + value;
- this가 {base: 20, rate:30}을 참조합니다.
- 20 * 30 + 50이 됩니다.
- this로 참조할 오브젝트를 변경할 수 있는 것이 call()의 특징입니다.
get 함수 자체의 코드는 바뀌지 않아도 되며, 참조 받은 this의 오브젝트만 바꿔 주는 것으로 데이터 중심의 처리가 가능하게 됩니다.
숫자 사용
function get() {
return this.valueOf();
};
var result = get.call(123);
console.log(result);
// 123
- var result = get.call(123);
- this가 오브젝트를 참조하므로 숫자(123)를 작성하면 에러가 발생하는게 맞습니다. 하지만 실제 동작을 시키면 에러가 나지 않습니다.
- 그 이유는 값 타입에 해당하는 Number 인스턴스를 생성후 123을 primitive 값으로 설정하여 this는 Number(123)을 참조하기 때문입니다.
this 참조 변경
var book = {
value: 123,
point: {
value: 456,
get: function() {
console.log(this.value);
}
}
};
book.point.get.call(book);
book.point.get.call(book.point);
// 123
// 456
- book.point.get.call(book);
book.point의 get()호출
get()에서 this로 book 오브젝트를 참조합니다.
this.value는 book.value이기에 123을 출력합니다.
- book.point.get.call(book.point);
book.point의 get() 호출
get()에서 this로 book.point 오브젝트를 참조합니다.
this.value가 book.point.value이기에 456을 출력합니다.
this와 apply() 메소드
this 바인딩 컴포넌트와 파라미터를 유동적으로 변경하며 로직을 수행하기 위해 사용하는 함수, 데이터 중심 접근
getTotal.apply(this, [10,20])
- 함수 호출 방법은 call()과 동일하며 파라미터가 배열인 것이 차이점입니다.
- [10,20]을 파라미터 값으로 넘겨줍니다.
두 번째 파라미터 수가 유동적일 때 사용합니다.
call()은 파라미터 수가 고정일 때 사용합니다.
this와 arguments
var obj = {0: 10, 1: 20, 2: 30};
var data = [4, 5, 6];
function get() {
for(k = 0; k < arguments.length; k++) {
console.log(arguments[k] + this[k]);
};
};
get.apply(obj, data);
// 14
// 25
// 36
- get.apply(obj, data);
- get()함수에서 obj를 this로 참조합니다.
- 두 번째 파라미터 [4,5,6]을 arguments를 사용하여 계산합니다.
- 파라미터 수가 유동적이므로 arguments가 편리합니다.
- get()의 함수 코드는 바뀌지 않으며 넘겨주는 파라미터 값과 this로 참조할 오브젝트만 변경하면 됩니다.
- obj는 Array-like 형태입니다.
this와 콜백함수
var obj = {value: 100};
var data = [5, 6, 7];
function callback(element, index, data) {
return element + this.value;
};
function get(data) {
return data.map(callback, obj);
};
var result = get(data);
console.log(result);
// [105, 106, 107]
- ES5의 map(), forEach() 처럼 콜백 함수가 있는 메소드는 두 번째 파라미터에 this로 참조할 오브젝트를 작성(optional)
- function callback(element, index, data) { return element + this.value; };
- return data.map(callback, obj);
- map()의 두 번째 파라미터에 obj를 작성합니다.(this 바인딩 컴포넌트)
- callback()에서 obj를 this로 참조합니다.
- map()의 코드는 바꾸지 않고 obj와 data 파라미터 값만 바꾸면 됩니다.
get(data)에서 배열을 받으며 map() 메소드를 사용할 수 있게 되는데, map의 본 기능은 콜백함수 호출과 this 참조 오브젝트로 경로, 즉 데이터를 넘겨주는 것 입니다.
따라서, map은 콜백함수와 독립성을 가지고 작동합니다. 확장하자면 ES5의 데이터 관점 접근의 콜백함수가 있는 7개의 메소드 사용을 통하여 보다 구체화된 처리가 가능합니다.
데이터 중심 처리
- this를 활용한 함수처리
- call(), apply() : 참조할 this를 넘겨줌
- 콜백함수가 있는 메소드 : 배열을 반복 + 참조할 this를 넘겨줌
- this를 활용한 묶음단위 처리
this와 bind() 메소드
bind 메소드는 '무엇을 묶는 것인가?'에 대해 생각해 볼 필요가 있습니다.
일반적으로 함수는 호출 즉시 실행하지만, bind() 메소드는 function 오브젝트를 만드는 1단계와 생성한 function 오브젝트를 함수로 호출하는 2단계가 존재합니다.
파라미터
- 1번째 파라미터에 함수에서 this로 참조할 오브젝트를 작성합니다.
- 2번째 파라미터에 호출된 함수의 파라미터 값을 작성합니다.
생성한 function을 호출할 때에도 파라미터 작성이 가능합니다.
- 두 개의 파라미터를 병합하여 사용합니다.
function 오브젝트 생성/호출
var book = {
point: 123,
get: function(){
return this.point;
}
};
var obj = book.get.bind(book);
console.log(typeof obj);
var result = obj();
console.log(result);
// 123
- var obj = book.get.bind(book);
- book.get()을 호출하지 않고 function 오브젝트를 생성하여 반환합니다.
- 생성한 function 오브젝트를 생성한 오브젝트의 [[TargetFunction]]에 설정합니다.
- 처리를 나누어 하기에 저장이 필요합니다.
- console.log(typeof obj);
obj의 타입은 function 오브젝트입니다.
- bind()의 첫 번째 파라미터
- get() 함수에서 this로 참조할 오브젝트를 작성합니다.
- get() 앞에 작성한 오브젝트를 this로 참조하지 않습니다(book)
- 작성하지 않으면 undefined로 설정됩니다.
- 생성한 function 오브젝트의 [[BoundThis]]에 설정됩니다.
- var result = obj();
- bind()로 생성한 function 오브젝트를 호출합니다.
- 이 때 경로가 연결된 book.get() 함수가 호출됩니다.
- return point;
- this가 [[BoundTHis]]에 설정된 book 오브젝트를 참조하므로 123이 반환됩니다.
파라미터 병합
var book = {
get: function() {
return Array.prototype.slice.call(arguments);
}
};
var obj = book.get.bind(this, 10, 20);
var result = obj(30, 40);
console.log(result);
- var obj = book.get.bind(this, 10, 20);
- 두 번째, 세 번째 파라미터에 값을 작성했으며 book.get()의 파라미터 값으로 넘겨줍니다.
- function 오브젝트의 [[BoundArguments]]에 설정됩니다.
- get() 함수에 파라미터 이름을 작성하지 않고 arguments를 사용합니다.
- return Array.prototype.slice.call(arguments);
- slice()는 배열형 객체 형태로 이루어진 인덱스 범위의 arguments 엘리먼트를 배열로 변환하여 반환합니다.
- 인덱스를 작성하지 않으면 arguments 전체를 반환합니다.
- var result = obj(30, 40)
- book.get() 함수가 호출되며 book.get.bind(this, 10, 20);에서 10과 20을 [10,20]형태로 반환합니다
- 여기에 obj(30, 40)의 30과 40을 병합(첨부)하여 반환합니다.
bind() 활용, 이벤트 처리
<script src="point.js" defer></script>
<button id="point">값 출력</button>
var book = {
myPoint: 100,
// 이벤트 설정
setEvent: function() {
var node = document.getElementById("point");
node.onclick = this.show.bind(book, node);
},
// 핸들러 함수
show: function(node, event) {
console.log(node.textContent);
console.log(this.myPoint);
}
};
book.setEvent()
// 값 출력
// 100
- 예제
- 이벤트 처리의 어려움은 이벤트를 설정할 때의 오브젝트를 핸들러에서 this로 참조 할 수 없다는 것 입니다.
- document.getElementById("point");
- button#point로 엘리먼트 오브젝트 생성
- node.onclick = this.show.bind(book, node);
- show()는 onclick 이벤트의 핸들러
- show()에서 this로 book 오브젝트를 참조하기 위해 바인드합니다.
- show() 파라미터 값으로 node를 넘겨줍니다.
- show: function(node, event) {
- button#point를 클릭했을 때 호출됩니다.
- node: 이벤트를 설정한 엘리먼트
- event: Event 오브젝트(onclick, mouseover ... )
- console.log(this.myPoint);
- bind() 첫 번째 파라미터에 book 오브젝트를 작성했으며 이를 this로 참조하므로 100이 표시됩니다.
이벤트 처리 시 이벤트 설정 함수에 bind()를 사용하여 심플하게 핸들러 함수를 묶을 수 있습니다. 핸들러 함수는 독립적으로 사용할 수 있도록 구성하고, this로 참조 + 파라미터 등 다양한 값들을 참조하여 사용하게 설계할 수 있습니다.