Javascript this

Sulhwa Choi·2022년 10월 21일
0

함수 호출 방식에 따라 결정되는 this 💨

✋ this 란?

-> 함수를 선언되었을 때가 아닌 호출되었을때 결정
-> ES5는 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드를 도입
-> ES2015는 스스로의 this 바인딩을 제공하지 않는 화살표 함수를 추가


1. 함수 호출

전역객체(Global Object)는 모든 객체의 유일한 최상위 객체를 의미한다

  • Browser-side에서는 window객체
  • Server-side(Node.js)에서는 global객체
function foo() {
  console.log("foo's this: ",  this);  // window
  function bar() {
    console.log("bar's this: ", this); // window
  }
  bar();
}
foo();


👉 기본적으로 this 는 전역객체에 바인딩 되고, 내부함수의 this 도 전역객체에 바인딩 된다.

//내부함수
var value = 1;

var obj = {
  value: 100,
  foo: function() {
    console.log("foo's this: ",  this);  // obj
    console.log("foo's this.value: ",  this.value); // 100
    function bar() {
      console.log("bar's this: ",  this); // window
      console.log("bar's this.value: ", this.value); // 1
    }
    bar();
  }
};

obj.foo();

----------------------------------------------------

//콜백함수
var value = 1;

var obj = {
  value: 100,
  foo: function() {
    setTimeout(function() {
      console.log("callback's this: ",  this);  // window
      console.log("callback's this.value: ",  this.value); // 1
    }, 100);
  }
};

obj.foo();

🔥 this가 전역 객체에 바인딩 되는 경우

  • 전역 함수에서의 this

  • 내부 함수(중첩 함수)에서의 this

  • 메서드의 내부 함수에서의 this

  • 콜백 함수에서의 this

  • 이벤트에서의 this는 '이벤트 객체'

  • 메서드에서의 this는 '메서드 객체'

2. 메소드 호출

🫧 메서드 내부에서의 this

  • this에는 호출한 주제에 대한 정보가 담기는데, 어떤 함수를 메서드로서 호출할때의 호출주체는 함수명(프로퍼티명) 앞의 객체이다.
  • 점 표기법의 경우 마지막 점 앞에 명시된 객체가 곧 this가 된다.

//메소드 내부 this
var obj1 = {
  name: 'Lee',
  sayName: function() {
    console.log(this.name); //Lee
  }
}

var obj2 = {
  name: 'Kim'
}

obj2.sayName = obj1.sayName; //Kim

obj1.sayName();
obj2.sayName();

👉 함수가 객체의 프로퍼티 값일 시, 메소드로서 호출되고, this는 메소드를 소유한 객체가 된다.

----------------------------------------------------

//프로토타입 객체
function Person(name) {
  this.name = name;
}

Person.prototype.getName = function() {
  return this.name;
}

var me = new Person('Lee');
console.log(me.getName()); //Lee

Person.prototype.name = 'Kim';
console.log(Person.prototype.getName()); //Kim

👉 프로토타입 객체도 메소드를 가질 수 있고, 프로토타입 객체의 this도 해당 메소드를 호출한 객체를 바인딩한다.

3. 생성자 함수 호출

  • 어떤 공통된 성질을 지니는 객체들을 생성하는데 사용하는데 함수이고, 자바스크립트에서는 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.
  • 객체지향 언어에서는 생성자를 클래스, 클래스를 통해 만든 객체를 인스턴스라고 한다.

어떤 함수가 생성자 함수로서 호출된 경우 내부에서의 this는 곧 새로 만들 구체적인 객체(인스턴스) 자신이다.

// 생성자 함수
function Person(name) {
  // 생성자 함수 코드 실행 전 -------- 1
  this.name = name; // --------- 2
  // 생성된 함수 반환 -------------- 3
}

var me = new Person('Lee');
console.log(me); // Person {name: 'Lee'}

// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person('Kim');
console.log(you); // undefined

👉 new 연산자와 함께 생성자 함수를 호출하면 this 바인딩이 메소드나 함수 호출 때와는 다르게 동작한다.
  1. 객체 리터럴 방식과 생성자 함수 방식의 차이
// 객체 리터럴 방식
var foo = {
  name: 'foo',
  gender: 'male'
}

console.dir(foo); //Object

// 생성자 함수 방식
function Person(name, gender) {
  this.name = name;
  this.gender = gender;
}

var me  = new Person('Lee', 'male');
console.dir(me); //Person

var you = new Person('Kim', 'female');
console.dir(you);  //Person


/* 객체 리터럴 방식과 생성자 함수 방식의 차이는 프로토타입 객체([[Prototype]])에 있다.

객체 리터럴 방식의 경우, 생성된 객체의 프로토타입 객체는 Object.prototype이다.

생성자 함수 방식의 경우, 생성된 객체의 프로토타입 객체는 Person.prototype이다.

프로토타입이 다르므로 상위 객체가 다르다
*/

가장 큰 차이점은 재사용 가능 여부입니다. 객체 리터럴 방식으로 객체를 생성하는 방법은 재사용할 수 없지만, 생성자 함수는 재사용할 수 있습니다.

  1. 생성자 함수에 new 연산자를 붙이지 않고 호출할 경우

    생성자 함수는 new 연산자를 붙여서 함수를 호출하면 return문이 없어도 객체가 반환된다는 점 이외에는 일반 함수와 큰 차이점이 없다. (new 연산자를 사용해야 객체가 반환된다)

function Person(name, gender) {
  this.name = name;
  this.gender = gender;
}

var me  = Person('Lee', 'male');
console.log(me); // undefined

👉 javascript 함수에 return문이 존재하지 않을 시 함수는 undefined를 반환한다.

4. apply/call/bind 호출 📍

  • this에 바인딩될 객체는 함수 호출 패턴에 의해 결정되는데, 자바스크립트 엔진의 암묵적 this 바인딩 이외에 this를 특정 객체에 명시적으로 바인딩하는 방법도 제공된다.

Function.prototype.apply, Function.prototype.call, Function.prototype.bind 메소드는 모든 함수 객체의 프로토타입 객체인 Function.prototype 객체의 메소드이다.

  1. apply 메서드
    call 메서드와 기능적으로 완전히 동일하며, call 메서드는 첫 번째 인자를 제외한 나머지 모든 인자들을 호출할 함수의 매개변수로 지정하는 반면, apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 점에서만 차이가 있다.
Person.apply(foo, [1, 2, 3]);

Person.call(foo, 1, 2, 3);```

apply() 메소드는 this를 특정 객체에 바인딩할 뿐 본질적인 기능은 함수 호출이다.

var Person = function (name) {
  this.name = name;
};

var foo = {};

// apply 메소드는 생성자함수 Person을 호출, 이때 this에 객체 foo를 바인딩한다.
Person.apply(foo, ['name']);

console.log(foo); // { name: 'name' }
  1. call 메서드
    메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령.
    call 메서드의 첫 번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 한다. 함수를 그냥 실행하면 this는 전역객체를 참조하지만 call 메서드를 이용하면 임의의 객체를 this로 지정할 수 있다.
function Person(name) {
  this.name = name;
}

Person.prototype.doSomething = function(callback) {
  if(typeof callback == 'function') {
    // --------- 1
    callback();
  }
};

function foo() {
  console.log(this.name); // --------- 2
}

var p = new Person('Lee');
p.doSomething(foo);  // undefined

1에서 this는 Person 객체이다.
2에서 this는 전역 객체 window를 가리킨다.
기본적으로 콜백함수 내부의 this는 window를 가리킨다

// --------- 1
callback.call(this);
콜백함수 내부의 this를 콜백함수를 호출하는 함수 내부 this와 일치시킨다.

// --------- 1
callback.bind(this)();
  1. bind 메서드
function Person(name) {
  this.name = name;
}

Person.prototype.doSomething = function (callback) {
  if (typeof callback == 'function') {
    // callback.call(this);
    // this가 바인딩된 새로운 함수를 호출
    callback.bind(this)();
  }
};

function foo() {
  console.log('#', this.name);
}

var p = new Person('Lee');
p.doSomething(foo);  // 'Lee'

👉 함수에 인자로 전달한 this가 바인딩된 새로운 함수를 리턴, 
  Function.prototype.bind는 Function.prototype.apply, Function.prototype.call 메소드와 같이 함수를 실행하지 않기 때문에 명시적으로 함수를 호출할 필요가 있다.
profile
개발 공부 중 〰️ ٩(๑•̀o•́๑)و ✨

0개의 댓글