본문 바로가기
FrontEnd/2024

javascript에서 this 컨트롤 하기

by cariño 2024. 4. 1.
728x90
반응형

* 해당 글은 정리가 잘 된 포스팅 글 전체를 가져온 글입니다. (공부를 위해 타이핑..)

같은 글이니 아래 링크에서 포스팅 확인하세요 :)

https://poiemaweb.com/js-this

 

 

 

this는 매개변수로 전달되는 인자값 이외에 argument객체와 this를 암묵적으로 전달 받는다. 

this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로터피나 메서드를 참조할 수 있다. 

크게보면 전역에서 사용할 때와 함수안에서 사용할 때로 나눌 수 있다. 

 

 

자바스크립트에서 this는 함수 호출 방식에 따라 바인딩되는 객체가 달라진다.

1. 전역에서 선언된 함수에서의 this는 global(window, global)이다.

2. 함수 내부에서 사용하는 this

- 엄격모드에서 일반 내부함수의 this는 undefined이다.

- 객체 안에서 선언된 함수는 메소드라 하지만 결국 윈도우 전역 객체의 메소드 이다. this는 현재 실행하고 있는 해당 객체를 참조하는데 this에 바인딩할 객체가 정적으로 결정되는 것이 아닌,  함수 내부에서 this를 호출방식에 의해 this에 바인딩할 객체는 동적으로 결정된다함수의 상위 스코프를 결정하는 렉시컬 스코프는 함수를 선언할 때 결정된다. 

함수의 호출하는 방식

1. 함수호출
2. 메서드 호출
3. 생성자 함수 호출
4. apply, call, bind호출

 

1. 함수 호출

전역객체는 모든 객체의 유일한 최상위 객체를 의미한다. 브라우저에서는 window를 서버사이드에서는 global 객체를 의미한다. 

기본적으로 this는 전역객체에 바인딩되고 내부함수 또한 외부함수가 아닌 전역객체에 바인딩된다.

메소드 내부 함수의 경우에도 this는 전역객체에 바인딩 되고 콜백함수 또한 this는 전역객체에 바인딩된다.  

function foo() {
  console.log("foo's this: ",  this);  // window
  function bar() {
    console.log("bar's this: ", this); // window
  }
  bar();
}
foo();
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();
  },
  callbackFn: function() {
    setTimeout(function() {
      console.log("callback's this: ",  this);  // window
      console.log("callback's this.value: ",  this.value); // 1
    }, 100);
  }
};

obj.foo();
obj.callbackFn()

 

 

2. 메소드 호출

함수가 객체의 프로퍼티 값이면 메소드로서 호출된다. 이 때 this는 메소드를 소유한 객체에 바인딩된다. 

프로토타입 객체 또한 메소드 내부에서 사용된 this도 호출한 객체에 바인딩 된다. 

const fn = {
 title: 'hello world',
 showTit(){
   console.log(this.title)
  }
}

fn.showTit() //'hello world'

 

3. 생성자 함수 호출

생성자함수는 말 그대로 객체를 생성하는 역할을 한다. new연산자와 함께 첫문자를 대문자로 함수명을 만들어 생성하면 된다. 

new연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다. 

 

[생성자 함수 동작 방식]

1). 생성자 함수는 코드가 실행되기 전 빈 객체를 생성한다. 생성자 함수 내에서 사용되는 this는 빈 객체를 가리킨다.  빈 객체는 생성자 함수의 프로토타입 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다. 

2). 생성된 빈 객체에 this를 사용하여 동적으로 프로퍼티나 메소드를 생성할 수 있다. 

3). 반환문이 없는 경우 this에 바인딩 된 새로운 객체가 반환된다.  반환문이 this가 아닌 다른 객체를 명시적으로 반환하는 경우는 해당 객체가 반환된다. 생성자 함수는 this를 반환하는 역할을 갖고 있기에 반환문을 명시적으로 사용하지 않는다. 

 

[객체 리터럴과 생성자 함수 방식의 차이]

  • 객체 리터럴 방식의 경우, 생성된 객체의 프로토타입 객체는 Object.prototype이다.
  • 생성자 함수 방식의 경우, 생성된 객체의 프로토타입 객체는 Person.prototype이다.
// 객체 리터럴 방식
var foo = {
  name: 'foo',
  gender: 'male'
}

console.dir(foo);

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

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

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

[생성자 함수에 new를 붙이지 않고 호출할 경우]

객체 생성의 목적인 생성자 함수는 new 없이 호출하거나 일반함수에 new를 붙이면 오류가 발생할 수 있다. 

일반함수와 생성자 함수의 호출 시의 this 바인딩은 다르기 때문이다. 

 

일반함수는 windows객체에 바인딩되지만 new연산자와 함께 생성된 생성자 함수를 호출하면 this는 암묵적으로 빈 객체에 바인딩 된다. 

new없이 생성자 함수를 호출하게 된다면,  함수 내부 this는 전역객체를 가리키므로 전역변수가 바인딩된다. 

일반 함수와의 차이점을 잘 알아두고 혼란을 방지해야한다. 

위험성을 회피하기 위해 사용되는 패턴 (scope-safe constructor)

 

 

4. apply/ call/ bind호출

자바스크립트 엔진은 암묵적으로 this에 바인딩이외에 this를 명시적으로 바인딩하는 방법도 제공한다. 

함수 객체의 프로토타입 객체인 Function.prototype을 사용한다.

Function.prototype.apply, Function.prototype.call 

 

func.apply(thisArg, [argsArray])

- thisArg: 함수 내부의 this에 바인딩할 객체

- argsArray: 함수에 전달할 argument 의 배열

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

 

var Person = function(name) {
 this.name = name
 }
 
 var foo = {}
 
 Person.apply(foo, ['name'])
 console.log(foo) // {name: 'name'}

apply메소드는 생성자 함수 Person을 호출한다. 이때 this에 객체 foo를 바인딩 시킨다. 

this에 바인딩된 foo객체는 name프로퍼티가 없으므로 동적으로 값이 추가되고 할당된다. 

 

function convertArgsToArray() {
  console.log(arguments);

  // arguments 객체를 배열로 변환
  // slice: 배열의 특정 부분에 대한 복사본을 생성한다.
  var arr = Array.prototype.slice.apply(arguments); // arguments.slice
  // var arr = [].slice.apply(arguments);

  console.log(arr);
  return arr;
}

convertArgsToArray(1, 2, 3);

apply() 메소드의 대표적인 용도는 유사배열객체에 배열 메소드를 사용하는 경우이다.

this는 arguments 객체에 바인딩하라의 의미가 되고 Array.prototype.slice() 메소드를 arguments객체 자신의 메소드인것처럼 argument.slice()와 같은 형태로 호출하라는 것이된다.

 

 

func.apply(thisArg, argument, argument, argument)

apply()와 call()메소드는 콜백 함수의 this를 위해서 사용되기도 한다. 

단순히 콜백 함수의 this는 windows를 가리킨다. 호출하는 외부 함수와 내부 함수의 this가 서로 다르기 때문에 콜백함수 내부의 this를 콜백함수 호출하는 함수 내부의 this와 일치 시켜주어야 한다. 

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

Person.prototype.doSomething = function (callback) {
  if (typeof callback == 'function') {
    callback.call(this);
  }
};

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

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

 

 

Function.prototype.bind

ES5에서 추가된 방법이다. 함수에 인자로 전달한 this가 바인딩된 새로운 함수를 리턴한다. 

Function.prototype.bind는 apply(), call()메소드처럼 함수를 실행하지 않기 때문에 명시적으로 함수를 호출을 해야한다.

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'

 

728x90

댓글