This

이재윤·2021년 5월 7일
0

JavaScript

목록 보기
3/10
post-thumbnail

JavaScript의 this는 함수의 선언위치(Lexical Scope)와 관련있는 것이 아니라, 호출위치에 따라 동적으로 결정된다.

💻 기본 바인딩

this는 전역객체를 기본으로 참조하고 있다.

function foo() {
  console.log(this.a)
}
var a = 2
foo() // 2

💻 암시적 바인딩

함수 레퍼런스에 대한 객체가 존재할 때 암시적 바인딩 규칙에 따르며, 함수 호출 시 객체가 this에 바인딩 된다.

function foo() {
  console.log(this.a)
}

var a = 'this is global'

var obj = {
  a: 'this is local',
  foo: foo
}
obj.foo() // this is local

객체내부에서 객체를 참조할 때 최상위, 최하위의 정보가 호출된다.

function foo() {
  console.log(this.a)
}

var obj2 = {
  a: 'this is obj2',
  foo: foo
}
var obj1 = {
  a: 'this is obj1',
  obj2: obj2
}
obj1.obj2.foo() // this is obj2

❗ 암시적 소실

암시적으로 바인딩 된 경우 바인딩이 소실 되는 경우가 있다.

function foo() {
  console.log(this.a)
}
var obj = {
  a: 'this is local',
  foo: foo
}
var a = 'this is global'
var bar = obj.foo
bar() // this is global

barobj객체의 foo함수를 참조하는 것 처럼 보이나 foo함수를 직접 가리키고 있는 것이다.

function foo() {
  console.log(this.a)
}
function callBack(fn) {
  fn()
}
var obj = {
  a: 'this is local',
  foo: foo
}
var a = 'this is global'
callBack(obj.foo) // this is global

callBack 함수의 파라미터 fn는 위 예시의 bar변수와 같이 foo함수를 직접 가리키는 또다른 참조값이다.

실제 많이 볼수 있는 예시는 아래와 같다.

function foo() {
  console.log(this.a)
}

var obj = {
  a: 'this is local',
  foo: foo
}
var a = 'this is global'
setTimeout(obj.foo, 1000); // this is global

💻 명시적 바인딩

객체를 직접 지정하여 바인딩하는 방식이다.

var a = 'this is global'

function foo() {
  console.log(this.a)
}
var obj = {
  a: 'this is local'
}
foo.call(obj) // this is local

foo.call()에서 obj를 직접지정하여 this에 바인딩 했으므로 foo.call()this는 항상 obj이다.

❗ 하드 바인딩

암시적 바인딩의 문제였던 소실을 해결할 수 있는 방법이다.

var a = 'this is global'

function foo() {
  console.log(this.a)
}

var obj = {
  a: 'this is local'
}

var bar = function() {
  foo.call(obj)
}
bar() // this is local
setTimeout(bar, 100) // this is local

함수 bar는 내부에서 obj로 바인딩된 함수 foo를 호출하고 있다. 이 경우 bar를 어떤식으로 호출하더라도 obj로 바인딩된 foo를 실행하게 된다.

바인딩할 객체를 동적으로 결정하고 싶은 경우 아래와 같은 함수를 사용할 수 있다.

function foo(something) {
  console.log(this.a, something)
  return this.a + something
}
function bind(fn, obj) {
  return function() {
    return fn.apply(obj, arguments)
  }
}
var obj = {
  a: 1
}
var bar = bind(foo, obj)
var b = bar(2) // 1 2
console.log(b) // 3

❗ 하드 바인딩은 자주 사용되는 패턴이여서 ES5 내장 함수로 Function.prototype.bind가 구현되어 있다.

❗ API 호출 컨택스트
JavaScript 내장 함수는 thisArgs라는 선택적 인자를 제공한다. 이는 callback을 호출할 때 전달해 this의 값으로 쓰인다.

arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])

function foo(el) {
  console.log(el, this.id)
}

var obj = {
  id: "wizard"
}

["harry", "ron", "hermione"].forEach(foo, obj)
// harry wizard
// ron wizard
// hermione wizard

💻 new 바인딩

function foo(a) {
  this.a = a
}
var bar = new foo(2)
console.log(bar.a) // 2

새로 생성된 객체 barthis에 바인딩 된다.

💻 규칙 우선순위

🧪 명시적 바인딩 VS 암시적 바인딩
function foo() {
  console.log(this.a)
}
var obj1 = {
  a: "this is obj1",
  foo: foo
}
var obj2 = {
  a: "this is obj2",
  foo: foo
}

obj1.foo() // this is obj1
obj2.foo() // this is obj2

obj1.foo.call(obj2) // this is obj2
obj2.foo.call(obj1) // this is obj1

명시적 바인딩이 암시적 바인딩보다 우선순위가 높다.

🧪 new 바인딩 VS 암시적 바인딩
function foo(something) {
  this.a = something
}

var obj1 = {
  foo: foo
}
var obj2 = {}
obj1.foo('this is obj1')
console.log(obj1.a) // this is obj1

obj1.foo.call(obj2, 'this is obj2')
console.log(obj2.a) // this is obj2

var bar = new obj1.foo('this is bar')
console.log(bar.a) // this is bar
console.log(obj1.a) // this is obj1

new 바인딩이 암시적 바인딩 보다 우선 순위가 높다.

🧪 new 바인딩 VS 명시적 바인딩
function foo(something) {
  this.a = something
}
var obj1 = {}
var bar = foo.bind(obj1)
bar('this is obj1')
console.log(obj1.a) // this is obj1

var baz = new bar('this is baz')
console.log(obj1.a) // this is obj1
console.log(baz.a) // this is baz

new 바인딩이 명시적 바인딩 보다 우선 순위가 높다.

💻 this 확정 규칙

  1. new로 함수를 호출 했는지 → 맞으면 새로 생성된 객체가 this
var bar = new foo()
  1. call, apply, bind를 이용해 함수를 호출 했는가? → 맞으면 명시적으로 지정된 객체가 this다.
var bar = foo.call(obj2)
  1. 함수를 컨텍스트 형태로 호출 했는가? → 맞으면 컨텍스트 객체가 this다.
var bar = obj1.foo()
  1. 그 외의 경우는 this는 기본값으로 설정된다.
var bar = foo()

💻 화살표 함수

일반 함수와는 달리 화살표 함수의 this 언제나 상위 스코프의 this를 가리킨다. 이를 Lexical this라 한다. Lexical scope와 유사하다.

function foo() {
  return (a) => {
    // this는 화살표함수의 상위 스코프인 foo 함수에 종속된다.
    console.log(this.a)
  }
}

var obj1 = {
  a: 'this is obj1'
}

var obj2 = {
  a: 'this is obj2'
}
var bar = foo.call(obj1)
bar.call(obj2) // this is obj1
function foo() {
  // foo의 this를 따른다.
  setTimeout(() => {
    console.log(this.a)
  }, 100)
}
var obj = {
  a: 'this is obj'
}
foo.call(obj) // this is obj

❗ 예외사항

const person = {
  name: 'Lee',
  // 상위 스코프인 글로벌이 this로 지정된다.
  sayHi: () => console.log(`I'm ${this.name}`)
};

person.sayHi(); // I'm undefined

화살표 함수는 객체의 프로퍼티로 지정하거나, prototype 프로퍼티로 지정할 경우 예측과 다르게 동작할 수 있으며, 생성자 함수로 사용할 수 없다.

참고자료

YOU DON'T KNOW JS
https://poiemaweb.com/es6-arrow-function

0개의 댓글