[JS] #7 This

개요

  • 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와 인스턴스

  • 인스턴스의 목적
    인스턴스마다 고유 값을 유지

  • 인스턴스에서 this의 목적
    this로 인스턴스를 참조하여 this.name형태로 프로퍼티에 접근할 수 있습니다.

  • __proto__ 프로퍼티 접근

    • new 연산자를 사용해 인스턴스를 생성하면 prototype에 연결된 프로퍼티가 인스턴스의 __proto__에 첨부됩니다.
    • this.method() 형태로 __proto__에 첨부된 method()를 호출할 수 있습니다.
    • prototype에 연결된 메소드는 모든 인스턴스에 공유

⇒ 일관된 환경에서 값만 다르게 가져가는 데이터 중심의 처리

관계 생성 과정

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
  1. var obj = new book.Point(100);
    • book.Point 인스턴스를 생성합니다.
  2. this.point = point;
    • this가 생성한 인스턴스를 참조하므로 point는 인스턴스 프로퍼티가 됩니다.
    • 이 논리로 인스턴스마다 프로퍼티 이름과 값을 유지할 수 있습니다.
  3. obj.getPoint();
    • obj 인스턴스의 getPoint() 메소드를 호출
  4. 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
  1. get.call(this, 20)
    • 첫 번째 파라미터에 this 작성합니다.
  2. 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
  1. var result = get.call(value, 50);
    • call()의 첫 번째에 Object를 작성합니다.
    • 50은 파라미터 값입니다.
  2. return this.base * this.rate + value;
    • this가 {base: 20, rate:30}을 참조합니다.
    • 20 * 30 + 50이 됩니다.
  3. this로 참조할 오브젝트를 변경할 수 있는 것이 call()의 특징입니다.

get 함수 자체의 코드는 바뀌지 않아도 되며, 참조 받은 this의 오브젝트만 바꿔 주는 것으로 데이터 중심의 처리가 가능하게 됩니다.

숫자 사용

function get() {
  return this.valueOf();
};
var result = get.call(123);
console.log(result);
// 123
  1. 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
  1. book.point.get.call(book);
    book.point의 get()호출
    get()에서 this로 book 오브젝트를 참조합니다.
    this.value는 book.value이기에 123을 출력합니다.
  2. 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
  1. get.apply(obj, data);
    • get()함수에서 obj를 this로 참조합니다.
  2. 두 번째 파라미터 [4,5,6]을 arguments를 사용하여 계산합니다.
    • 파라미터 수가 유동적이므로 arguments가 편리합니다.
  3. get()의 함수 코드는 바뀌지 않으며 넘겨주는 파라미터 값과 this로 참조할 오브젝트만 변경하면 됩니다.
  4. 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]
  1. ES5의 map(), forEach() 처럼 콜백 함수가 있는 메소드는 두 번째 파라미터에 this로 참조할 오브젝트를 작성(optional)
  2. function callback(element, index, data) { return element + this.value; };
    • map()에서 호출하는 콜백 함수
  3. return data.map(callback, obj);
    • map()의 두 번째 파라미터에 obj를 작성합니다.(this 바인딩 컴포넌트)
    • callback()에서 obj를 this로 참조합니다.
  4. map()의 코드는 바꾸지 않고 obj와 data 파라미터 값만 바꾸면 됩니다.

get(data)에서 배열을 받으며 map() 메소드를 사용할 수 있게 되는데, map의 본 기능은 콜백함수 호출과 this 참조 오브젝트로 경로, 즉 데이터를 넘겨주는 것 입니다.

따라서, map은 콜백함수와 독립성을 가지고 작동합니다. 확장하자면 ES5의 데이터 관점 접근의 콜백함수가 있는 7개의 메소드 사용을 통하여 보다 구체화된 처리가 가능합니다.

데이터 중심 처리

  • this를 활용한 함수처리
    • call(), apply() : 참조할 this를 넘겨줌
    • 콜백함수가 있는 메소드 : 배열을 반복 + 참조할 this를 넘겨줌
  • this를 활용한 묶음단위 처리
    • class
    • 인스턴스

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
  1. var obj = book.get.bind(book);
    • book.get()을 호출하지 않고 function 오브젝트를 생성하여 반환합니다.
    • 생성한 function 오브젝트를 생성한 오브젝트의 [[TargetFunction]]에 설정합니다.
    • 처리를 나누어 하기에 저장이 필요합니다.
  2. console.log(typeof obj);
    obj의 타입은 function 오브젝트입니다.
  3. bind()의 첫 번째 파라미터
    • get() 함수에서 this로 참조할 오브젝트를 작성합니다.
    • get() 앞에 작성한 오브젝트를 this로 참조하지 않습니다(book)
    • 작성하지 않으면 undefined로 설정됩니다.
    • 생성한 function 오브젝트의 [[BoundThis]]에 설정됩니다.
  4. var result = obj();
    • bind()로 생성한 function 오브젝트를 호출합니다.
    • 이 때 경로가 연결된 book.get() 함수가 호출됩니다.
  5. 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);
  1. var obj = book.get.bind(this, 10, 20);
    • 두 번째, 세 번째 파라미터에 값을 작성했으며 book.get()의 파라미터 값으로 넘겨줍니다.
    • function 오브젝트의 [[BoundArguments]]에 설정됩니다.
  2. get() 함수에 파라미터 이름을 작성하지 않고 arguments를 사용합니다.
  3. return Array.prototype.slice.call(arguments);
    • slice()는 배열형 객체 형태로 이루어진 인덱스 범위의 arguments 엘리먼트를 배열로 변환하여 반환합니다.
    • 인덱스를 작성하지 않으면 arguments 전체를 반환합니다.
  4. 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로 참조 할 수 없다는 것 입니다.
    • 이를 bind()로 해결할 수 있습니다.
  • 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로 참조 + 파라미터 등 다양한 값들을 참조하여 사용하게 설계할 수 있습니다.

좋은 웹페이지 즐겨찾기