this

y0ung·2021년 1월 10일
0

JavaScript

목록 보기
8/20
post-thumbnail

자바스크립트의 함수는 호출될 때, 매개변수로 전달되는 인자값 이외에, arguments객체와 this를 암묵적으로 전달 받는다.

let myFunction = function() {
  console.dir(arguments) // Arguments(0)
  console.log(this) // window
}   

myFunction()

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

함수 호출

전역 객체는 모든 객체의 유일한 최상위 객체를 의미하며 일반적으로 Brower-side에서는 window, Server-side(Node.js)에서는 global객체를 의미한다.

// in browser console
this === window // true

// in Terminal
node
this === global // true

전역객체는 전역 스코프를 갖는 전역변수를 프로퍼티로 소유하는데, 글로벌 영역에 선언한 함수는 전역 객체의 프로퍼티로 접근할수 있는 전역 변수의 메소드다.

기본적으로 this는 전역객체에 바인딩된다. 전역 함수는 물론이고 심지어 내부함수의 경우도 this는 전역객체에 바인딩된다.

function foo() {
  console.log("foo's this: ", this);  // window
  function bar() {
    console.log("bar's this: ", this); // window
  }
  bar();
}
foo();

내부 함수는 일반 함수, 메소드, 콜백함수 어디에서 선언되었든 상관없이 this는 전역 객체를 바인딩한다.내부함수에서 this가 전역객체를 참조하는것을 막는 방법

메소드의 내부 함수일 경우에도 this는 전역객체에 바인딩 된다.

var value = 1;

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

obj.foo();

var | let
예시에서 변수를 var로 선언하여서 나는 let으로 선언 했었다. 하지만 let으로 선언했을 경우 this.valueundefined나왔는데 이유는 varglobal전역 객체의 속성을 생성하지만,let은 전역객체의 속성을 생성하지 않기 때문이다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/let

콜백 함수의 경우에도 this는 전역 객체에 바인딩된다.

var value = 1;

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

obj.foo();

명시적 바인딩(Explicit binding)

내부함수는 일반 함수, 메소드, 콜백함수 어디에서 선언되었든 관게없이 this는 전역객체를 바인딩한다. 내부 함수의 this가 전역객체를 참조하는 것을 회피 하는 방법이다.

var value = 1;

var obj = {
  value: 100,
  foo: function() {
    var that = this; 

    console.log("foo's this: ",  this);  // obj
    
    function bar() {
      console.log("bar's this: ",  this); // window
      console.log("bar's that: ",  that); // obj
    }
    bar();
  }
};

obj.foo();

또다른 방법으로 apply, call, bind 메소드가 있다.

함수를 즉시 호출하기 위해서 call()/ apply() 메소드를 사용할수 있다. bind()는 함수가 나중에 실행됐을 때도 원본 함수를 호출할때 갖는 올바른 컨텍스트(this)가 bind된 함수를 반환한다. 그래서 bind()는 특정 이벤트에서 함수가 나중에 호출될 필요가 있을 때, 유용하다.

var obj = {name:"Olive"}

var greeting = function(a,b,c){
  return `Welcome ${this.name} to ${a} ${b} in ${c}`
}

// call
console.log(greeting.call(obj, "Newtown", "KOLKATA", "WB"));
// Welcome Olive to Newtown KOLKATA in WB

// apply
var agrs = ["Newtown", "KOLKATA", "WB"];
console.log(greeting.apply(obj,agrs));
// Welcome Olive to Newtown KOLKATA in WB

//bind
var bound = greeting.bind(obj);

console.dir(bound); // ƒ bound greeting()
console.log(bound("Newtown","KOLKATA", "WB")); // Welcome Olive to Newtown KOLKATA in WB

bind()메소드에 대한 위의 코드 예제에서 context를 가진 나중에 호출될 bound함수를 반환한다.

bind()메소드에 대한 첫번째 파라미터는 bound가 호출될 때, 타겟 함수에소 this의 값을 세팅하는 부분이다. bound함수가 "new" 연산자를 이용하여 생성됐을때는, 바인드 시킨 this값(첫번째 파라미터의값)이 무시된다. 나머지 파라미터들은 인자로 잘 넘겨진다.

메소드 호출

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

let obj1 = {
  name:'Lee',
  sayName:function(){
    console.log(this.name);
  }
}

let obj2 = {
  name:'Kim'
}

obj2.sayName = obj1.sayName;

obj1.sayName() // Kim
obj2.sayName() // Lee

![https://velog.velcdn.com/images%2Fgay0ung%2Fpost%2F63bb1e46-dd73-46bc-aeb6-8c1792955f60%2Fthumbnail-ts.png%5D(https%3A%2F%2Fimages.velog.io%2Fimages%2Fgay0ung%2Fpost%2F63bb1e46-dd73-46bc-aeb6-8c1792955f60%2Fthumbnail-ts.png)

생성자 함수 호출

자바스크립트의 생성자 함수는 말 그대로 객체를 생성하는 역할을 한다. 기존 함수에 new연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.

생성자 함수가 아닌 일반함수에 new연산자를 붙여 호출하면 생성자 함수처럼 동작할 수 있다. 따라서 생성자 함수명은 첫문자를 대문자로 기술하여 혼란을 방지해야 한다.

function Person(name,age){
  this.name = name 
  this.age = age
}

let Olive = new Person('Olive',27);
console.log(Olive);  // Person {name: "Olive", age: 27}

// new연산자를 사용하지 않으면 생성자 함수로 동작하지 않는다.
let Jenny = Person('Jenny',33);
console.log(Jenny); // undefined

생성자 함수 동작 방식

1. 빈객체 생성 및 this 바인딩
생성자 함수의 코드가 실행되기 전 빈객체 생성. 이 빈 객체가 생성자 함수가 새로 생성하는 객체이다. 이후 생성자 함수 내에서 사용되는 this는 이 빈객채를 가리킨다.

2.this를 통한 프로퍼티 생성
생성된 빈 객체에 this를 사용하여 동적으로 프로퍼티 또는 메소드를 생성할 수 있다.
this는 새로 생성된 객체를 가리키므로 this를 통해 생성한 프로퍼티와 메소드는 새로 생성된 객체에 추가된다.

3. 생성된 객체 반환
◾ 반환 문이 없는 경우, this에 바인딩된 새로 생성한 객체가 반환된다.
◾ 반환문이 this가 아닌 다른 객체를 명시적으로 반환하는 경우, this가 아닌 해당 객체가 반환된다. 이때 this를 반환하지 않은 함수는 생성자 함수로서의 역할을 수행하지 못한다. 따라서 생성자 함수는 반환문을 명시적으로 사용하지 않는다.

function Profile(name){
  //1. 생성자 함수 코드 실행전
  console.log(this) // Profile {} _ 단 생성자 함수 일 경우 this가 Profile을 가리킨다.
  this.name = name // 2. this를 통한 프로퍼티 생성
  // 3. 생성된 객체 반환
  // return 'lime' 일경우 lime을 반환한다.
}

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

객체 리터럴?

var myMethod = function () {
  console.log(this);
};

var myObject = {
 myMethod: myMethod
};

myMethod() // this === window
myObject.myMethod()// this === myObject

let boo = {
  name: 'boo',
  gender:'male'
} 

console.dir(boo);

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

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

차이점은?

  • 객체 리터럴 : 생성된 객체의 프로토타입 객체는 Object.prototype
  • 생성자 함수: 생성된 객체의 프로토타입 객체는 Person.prototype

생성자 함수를 new연산자 없이 호출하는 경우 생기는 오류의 위험성을 회피하기 위해 사용되는 패턴(Scope-Safe Constructor)은 다음과 같다

function A(arg){
  // new 연산자와 함께 호출되면 함수의 선두에서 빈객체를 생성하고 this에 바인딩 한다.
  // 만약 new연산자를 이용하지 않았을 경우
  if(!(this instanceof arguments.callee)){
    return new arguments.calle(are)
  }
}

let a = new A(100)

callee 는 arguments객체의 프로퍼티로서 함수 바디 내에서 현재 실행 중인 함수를 참조할 때 사용한다. 즉, 함수 바디내에서 현재 실행 중인 함수의 이름을 반환한다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/arguments/callee


참고

profile
어제보다는 오늘 더 나은

0개의 댓글