[JS] this (2) 메소드 호출 / 생성자 함수 호출 / apply, call, bind

준비해용·2023년 5월 2일

JS

목록 보기
2/2

2. 메소드 호출

  • 함수가 객체의 프로퍼티 값으로서 존재할 때 → 객체를 통해 함수를 호출하면 → 메소드로서 호출됩니다.
  • 메소드 내부의 this는, 해당 메소드를 호출하는 객체에 바인딩 됩니다.
var obj1 = {
	name: 'Lee', 
	getName() {
	// 메소드 내부의 this는 메소드를 호출한 객체에 바인딩된다
		return this.name;
	}
};

// sayName메소드를 obj1객체가 호출하고 있다 -> obj1에 객체에 바인딩된다
console.log(obj1.sayName()); // Lee
  • 메소드 내부에서 존재하는 this는 독립적인 별도의 객체지만, 활용하기 위해 다른 객체에 프로퍼티나 변수에 할당할 때는, 메소드가 포함된 객체에 접근해서 할당한다!
const otherObj = {
	name : 'Kim'
};

// otherObj에 새로운 프로퍼티 할당
otherObj.getName = obj1.getName; 

// 변수에 할당 
const getName = obj1.getName; 

// 일반함수로서! 호출
// 이때 this는 전역객체 window를 가리키게 됨! window.name의 기본값은 ''임 
console.log(getName()); // ''
  • 메소드 내부의 this는, 메소드가 포함된 객체와 관계 X ⇒ 메소드를 호출한 객체에 바인딩된다 기억하기!
function Person(name) {
    this.name = name;
}

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

var me = new Person('Lee');

// getName 메서드를 호출한 객체는 me다.
// 그래서 me객체의 name에 접근해서 값을 호출
console.log(me.getName()); // Lee

// Person.prototype의 name프로퍼티는 아직 undefined 
console.log(Person.prototype.getName()); // undefined

Person.prototype.name = 'Kim';

// getName 메서드를 호출한 객체 Person.prototype의 name프로퍼티에 접근
console.log(Person.prototype.getName()); // Kim
  • 프로토타입 객체도, 메소드와 프로퍼티를 가질 수 있음!

3. 생성자 함수 호출

💡 일반 함수에 new연산자를 이용해 인스턴스를 생성할 때, 해당 함수는 생성자 함수로서서 동작하게 됩니다. ( 그래서 생성자함수로 사용할 함수의 명칭은 첫문자를 대문자로 기술! 혼란방지 목적) 이 때, 함수 내부의 **this는 앞으로로 생성될 인스턴스에 바인딩**되도록 예정되어 있습니다. 아직 객체가 만들어지지 않았더라도! 바인딩되도록 정해져 있습니다다.

생성자 함수 호출을 통한 바인딩 확인하기

// 생성자 함수
function Circle1(radius) {
    // 생성자 함수 내부의 this는 생성자 함수가 앞으로 생성할 인스턴스를 가리킨다.
    this.radius = radius;
    this.getDiameter = function () {
      return 2 * this.radius; // this.radius는 상위의 값을 기리킴!
    };
  }

// this.radius를 없애고, 메소드 내부에서 radius로 바꾸면 똑같이 동작! 
function Circle2(radius) {
    this.getDiameter = function () {
      return 2 * radius; // <- this.radius
    };
}

// 반지름이 5인 Circle 객체를 생성
const circle1 = new Circle1(5);

// 반지름이 10인 Circle 객체를 생성
const circle2 = new Circle1(10);

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20

// new 연산자와 함께 호출하지 않으면 생성자 함수로 동작하지 않는다. 즉, 일반적인 함수의 호출이다.
const circle3 = Circle(15);

// 일반 함수로 호출된 Circle에는 반환문이 없으므로 암묵적으로 undefined를 반환한다.
console.log(circle3); // undefined

// 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다.
console.log(radius); // 15
console.log(getDiameter()) // 30

일반적인 함수의 호출로 사용할 때

// 일반적인 함수의 호출로 사용할 때
const circle3 = Circle1(15);

// 일반 함수로 호출된 Circle에는 반환문이 없으므로 암묵적으로 undefined를 반환한다.
console.log(circle3); // undefined

// 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다.
console.log(radius); // 15
console.log(circle3.radius) //TypeError: Cannot read properties of undefined (reading 'radius')
console.log(circle3.getDiameter()) // TypeError: Cannot read properties of undefined (reading 'getDiameter')

궁금해서 찍어본 console

function Circle3(radius) {
		this.radius3 = radius * 2
    return 3* radius;
}

console.log(Circle3(100)) // 300 
console.log(Circle3(100).radius3) // undefined

circle4 = Circle3(100)
console.log(circle4) // 300

apply/call/bind 메소드에 의한 간접 호출

💡 apply, call, bind 메소드는 모두 Function.prototype의 메소드이기 때문에,
모든 함수가 상속받아서 사용할 수 있습니다.

📕 개념

💡 apply와 call메소드의 본질적인 기능은 함수를 호출하는 것으로 같습니다.
다만 호출할 때 인수전달 방식에 차이가 있습니다.
apply메소드는 호출할 함수에 전달될 인수를 배열로 묶어서 전달하고,
call메소드는 인수를 쉼표로 구분해 리스트 형식으로 전달하면서 호출합니다.

apply, call과 함께 묶여서 언급되는 bind 메소드는 함수를 따로 호출하지 않고, this로 사용하고자 하는 객체만 담아서 전달합니다.

🔍 용도

💡 apply와 call메소드
arguments객체와 같은 유사 배열 객체에 배열 메소드를 사용하는 경우에 사용될 수 있습니다.

💡 bind 메소드
메소드 내부의 this와, 메소드 내부의 중첩함수/콜백함수의 this의 불일치에서 오는 문제를 해결하기 위해서 사용됩니다.

🦾 쓰임새

상속받아보기

function getThisBinding() {
    return this;
}
  
// this로 사용할 객체
const thisArg = { a: 1 };
//  console.log(getThisBinding()); // window 정보 출력
  
// getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다.
console.log(getThisBinding.apply(thisArg)); // {a: 1}
console.log(getThisBinding.call(thisArg)); // {a: 1}

apply, call 기본 사용 & 유사배열객체에 배열 메소드 사용

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

    const arr = **Array.prototype.slice.call**(arguments);
    // const arr = Array.prototype.slice.apply(arguments);
    console.log("함수내부 arr : ", arr); // 함수내부 arr :  [ 1, 2, 3 ]

    return this;
  }

// this로 사용할 객체
const thisArg = { a: 1 };
  
// getThisBinding 함수를 호출하면서, 인수로 전달한 객체(thisArg)를 함수의 this에 바인딩한다.

// **apply메서드** : 호출할 함수의 인수를 배열로 묶어 전달한다.
console.log(getThisBinding.apply(thisArg, [1, 2, 3]));
//   [Arguments] { '0': 1, '1': 2, '2': 3 }
//   { a: 1 } // <- 함수내부의 this

// **call 메서드** : 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다.
console.log(getThisBinding.call(thisArg, 1, 2, 3));
//   [Arguments] { '0': 1, '1': 2, '2': 3 }
//   { a: 1 } // <- 함수내부의 this

this 기본 & this불일치 문제 해결

function getThisBinding() {
  return this;
}

// this로 사용할 객체
const thisArg = { a: 1 };

// bind 메서드는 함수에 this로 사용할 객체를 전달한다.
// bind 메서드는 함수를 호출하지는 않는다.
console.log(getThisBinding.bind(thisArg)); // getThisBinding
// bind 메서드는 함수를 호출하지는 않으므로 명시적으로 호출해야 한다.
console.log(getThisBinding.bind(thisArg)()); // {a: 1}
const person = {
  name: 'Lee',
  foo(callback) {
    // bind 메서드로 callback 함수 내부의 this 바인딩을 전달
    setTimeout(callback.bind(this), 100);
  }
};

person.foo(function () {
  console.log(`Hi! my name is ${this.name}.`); // Hi! my name is Lee.
});

0개의 댓글