22.12.02 - this

Gon·2022년 12월 4일
0

Javascript

목록 보기
5/8
post-thumbnail

this

상황에 따라 달라지는 this

this는 실행 컨텍스트가 생성될 때 결정(this binding)
=== this는 함수를 호출할 때 결정

  1. 전역 공간에서의 this
    • 전역 공간에서 this는 전역 객체를 가리킴
    • window(브라우저 환경), global(node 환경)
  2. method로서 호출할 때 그 method 내부에서의 this
  • 함수 vs method
    1) 기준 : 독립성
    2) 함수 : 그 자체로 독립적인 기능을 수행
    3) method : 자신을 호출한 대상 객체에 관한 동작을 수행
var func = function (x) {
  console.log(this, x);
};
func(1); // Window { ... } 1

var obj = {
  method: func,
};
obj.method(2); // { method: f } 2
  • 함수로서의 호출과 method로서의 호출 구분 기준 : . []
var obj = {
  method: function (x) { console.log(this, x) }
};
obj.method(1); // { method: f } 1
obj['method'](2); // { method: f } 2
  • method 내부에서의 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
  1. 함수로서 호출할 때 그 함수 내부에서의 this
  • 함수 내부에서의 this
    1) 어떤 함수를 함수로서 호출할 경우, this는 지정되지 않음(호출 주체가 없으므로)
    2) 실행컨텍스트를 활성화할 당시 this가 지정되지 않은 경우, this는 전역 객체를 가리킴
    3) 따라서, 함수로서 ‘독립적으로’ 호출할 때는 this는 전역 객체

  • method의 내부함수에서의 this
    1) method의 내부라고 해도, 함수로서 호출한다면 this는 전역 객체
    2) this 바인딩에 관해서는
    함수를 실행하는 당시의 주변 환경(method 내부인지, 함수 내부인지)는 중요하지 않고,
    오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지가 중요함

var obj1 = {
  outer: function() {
    console.log(this); // obj1
    var innerFunc = function() {
      console.log(this);
    }
    innerFunc(); // 함수로서의 call -> this는 전역 객체
    
    var obj2 = {
      innerMethod: innerFunc
    };
    obj2.innerMethod();
  }
};
obj1.outer();
  • method의 내부 함수에서의 this 우회

1) 변수 활용

var obj1 = {
  outer: function() {
    console.log(this); // obj
    var innerFunc1 = function() {
      console.log(this);
    }
    innerFunc1(); // this === window
    
    var self = this;
    var innerFunc2 = function() {
      console.log(self);
    };
    innerFunc2(); // this === self
  }
};
obj1.outer();

2) 화살표함수
화살표 함수는, 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 없음

var obj = {
  outer: function() {
    console.log(this); // obj
    var innerFunc = () => {
      console.log(this);
    };
    innerFunc(); // this === obj
  }
}
obj.outer();
  1. 콜백 함수 호출 시 그 함수 내부에서의 this
setTimeout(function () { console.log(this) }, 300);

[1, 2, 3, 4, 5].forEach(function(x) {
  console.log(this, x);
});

document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function(e) {
  console.log(this, e);
});

1) setTimeout 함수, forEach method는 콜백 함수를 호출할 때 대상이 될 this를 지정하지 않으므로,
this는 곧 window객체

2) addEventListner method는 콜백 함수 호출 시, 자신의 this를 상속하므로, this는addEventListner의 앞부분
(button 태그)

  1. 생성자 함수 내부에서의 this
  • 생성자 : 구체적인 인스턴스를 만들기 위한 일종의 틀
  • 공통 속성들이 이미 준비돼 있음
var Dog = function (name, age) {
  this.bark = '멍멍';
  this.name = name;
  this.age = age;
};

var gae = new Cat('개', 6); //this : gae
var gangaji = new Cat('강아지', 2); //this : gangaji

명시적 this 바인딩

  1. call method
  • 호출 주체인 함수를 즉시 실행하는 명령어
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) {
    this 5
    console.log(this.a, x, y);
  }
};
obj.method(2, 3); // 1 2 3
obj.method.call({ a: 4 }, 5, 6); // 4 5 6
  1. apply method
  • call method와 완전 동일
  • 두 번째 인자가 배열인 부분만 다름
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
  1. call / apply method 활용
  • 유사배열객체(array-like-object)에 배열 method 적용
    ※ 유사배열의 조건
    1) 반드시 length가 필요하다.
    2) index 번호가 0부터 1씩 증가해야한다.
    ※ slice() 함수
    배열로부터 특정 범위를 복사한 값들을 담고 있는 새로운 배열을 만드는데 사용
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' ]
  • Array.from method(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 ab = new Student('abbie', 'female', '서울대');
var bc = new Employee('bictor', 'male', '삼성');
  • 여러 인수를 묶어 하나의 배열로 전달할 때 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)
//https://paperblock.tistory.com/62
const numbers = [10, 20, 3, 16, 45];
const max = Math.max(...numbers);
const min = Math.min(...numbers);
console.log(max min);
  1. bind method
  • call과 비슷하지만, 즉시 호출하지는 않고 넘겨받은 this 및 인수들로 새로운 함수를 반환하는 method

  • 목적
    1) 함수에 this를 미리 적용하는 것
    2) 부분 적용 함수 구현

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
  • name 프로퍼티
    • bind method를 적용해서 새로 만든 함수는 name 프로퍼티에 ‘bound’ 라는 접두어가 붙음
var func = function (a, b, c, d) {
  console.log(this, a, b, c, d);
};
var bindFunc = func.bind({ x:1 }, 4, 5);
  • 상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기

1) 내부함수

  • method의 내부함수에서 method의 this를 그대로 사용하기 위한 방법
  • self 등의 변수를 활용한 우회법보다 call, apply, 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();

2) 콜백함수

  • 콜백함수도 함수이기 때문에, 함수가 인자로 전달될 때는 함수 자체로 전달
  • bind method를 이용해 this를 입맛에 맞게 변경 가능
var obj = {
  logThis: function () {
    console.log(this);
  },
  logThisLater1: function () {
    setTimeout(this.logThis, 500);
  },
  logThisLater2: function () {
    setTimeout(this.logThis.bind(this), 1000);
  }
};

obj.logThisLater1();
obj.logThisLater2();
  • 화살표 함수의 예외사항
    1) 화살표 함수는 실행 컨텍스트 생성 시, this를 바인딩하는 과정이 제외
    2) 이 함수 내부에는 this가 아에 없으며, 접근코자 하면 스코프체인상 가장 가까운 this에 접근하게 됨
    3) this우회, call, apply, bind보다 편리한 방법
var obj = {
  outer: function () {
    console.log(this);
    var innerFunc = () => {
      console.log(this);
    };
    innerFunc();
  };
};
obj.outer();
  • 별도의 인자로 this를 받는 경우
    1) 콜백 함수를 인자로 받는 method 중 일부는 추가로 this로 지정할 객체를 인자로 지정할 수 있음
    2) 배열과 관련된 method에 많이 존재하며, set, map 등의 method에도 일부 존재함
// forEach 예시
var report = {
  sum: 0,
  count: 0,
  add: function () {
    var args = Array.prototype.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를 인자로 받는 method
// forEach, map, filter, some, every, find, findIndex, flatMap, from, forEach(Set, Map)

0개의 댓글