* 객체: 상태를 나타내는 프로퍼티와 동작을 나타내는 메서드를 하나의 논리적인 단위로 묶은 복합적인 자료구조
* 동작을 나타내는 메서드는 프로퍼티를 참조하고 변경할 수 있어야 한다.
--> 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.
const circle = {
// 프로퍼티: 객체 고유의 상태 데이터
radius: 5,
// 메서드: 상태 데이터를 참조하고 조작하는 동작
getDiameter() {
return 2 * circle.radius; // 자신이 속한 객체인 circle을 참조
}
};
console.log(circle.getDiameter()); // 10
--> 재귀적으로 객체를 참조하는 방식은 일반적이지 않다.
* 생성자 함수 방식으로 인스턴스 생성하는 경우
function Circle(radius) {
????.radius = radius;
}
Circle.prototype.getDiameter = function () {
return 2 * ????.radius;
}
// 생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수를 정의해야 한다.
const circle = new Circle(5);
--> 생성자 함수가 먼저 존재해야 한다. 생성자 함수를 정의하는 시점에는 아직 인스턴스를 생성하기 이전이므로 생성자 함수가 생성할 인스턴스를 가리킬 식별자를 알 수 없다!! -> this : 자신이 속한 객체, 자신이 생성할 인스턴스를 가리키는 식별자 (자기 참조 변수)
* this 바인딩은 함수 호출 방식에 의해 동적으로 결정
--> 바인딩 : 식별자와 값을 연결하는 과정
1. 객체 리터럴
const circle = { radius: 5, getDiameter() { return 2 * this.radius; // this는 메서드를 호출한 객체 } }; console.log(circle.getDiameter()); // 10
--> this는 메서드를 호출한 객체 : circle
2. 생성자 함수
// 생성자 함수 function Circle(radius) { this.radius = radius; // 생성자 함수가 생성할 인스턴스 } Circle.prototype.getDiameter = function () { return 2 * this.radius; // 생성자 함수가 생성할 인스턴스 가리킴 }; // 인스턴스 생성 const circle = new Circle(5); console.log(circle.getDiameter()); // 10
--> this는 생성자 함수가 생성할 인스턴스
--> 상황에 따라 바인딩이 다르게 됨. (동적으로 결정)
3. this 참조
1) 전역 -> window
2) 일반 함수 내부 -> window (일반함수에서는 의미 x)
3) 메서드 내부 -> 메서드를 호출한 객체
4) 생성자 함수 내부 -> 생성자 함수가 생성할 인스턴스
--> this 바인딩은 함수 호출 방식에 따라 동적으로 결정
렉시컬 스코프 vs this 바인딩
1) 렉시컬 스코프: 함수 정의가 펴악되어 함수 객체가 생성되는 시점
2) this 바인딩: 함수 호출 시점에 결정
1. 일반 함수 호출
--> this에 전역 객체가 바인딩된다.
* 일반 함수에서의 this는 의미가 없다. strict mode 에서는 일반 함수에서 this 에 undefined를 바인딩된다.
* 메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는 전역 객체가 바인딩된다.
어떠한 함수라도 일반 함수로 호출되면 this에 전역 객체가 바인딩된다!!
* 메서드 내부의 중첩 함수나 콜백 함수의 this 바인딩을 메서드의 this 바인딩과 일치시키기 위하여 that 키워드, apply, call, bind 메서드 사용
* 화살표 함수를 사용해서 this 바인딩을 일치시킬 수도 있다.
--> 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
2. 메서드 호출
this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩
const person = { name: 'Lee', getName() { return this.name; // 메서드 내부의 this는 메서드를 호출한 객체에 바인딩 } }; console.log(person.getName()); // Lee
--> getName 메서드는 객체에 포함된 것이 아니라 독립적으로 존재하는 별도의 객체이다.
--> getName 메서드는 다른 객체의 메서드가 될 수도 있고 일반 변수에 할당하여 일반 함수로 호출될 수도 있다.
--> ** 메서드 내부의 this는 프로퍼티로 메서드를 가리키고 있는 객체와는 관계가 없고, 메서드를 호출한 객체에 바인딩 (프로토타입 메서드도 마찬가지)// 생성자 함수 function Person(name) { this.name = name; } // 프로토타입 메서드 Person.prototype.getName = function () { return this.name; }; // 생성자 함수로 객체 생성 const me = new Person('Lee'); // 1번. console.log(me.getName()); // this는 me에 바인딩 (me가 메서드 호출함) // 2번. Person.prototype.name = 'Kim'; console.log(Person.prototype.getName()); // this는 Person.prototype에 바인딩 Kim
--> this에 바인딩될 객체는 호출 시점에 결정 !!
3. 생성자 함수 호출
--> 생성자 함수 내부의 this에는 생성자 함수가 미래에 생성할 인스턴스가 바인딩
// 생성자 함수 function Circle(radius) { // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스 가리킴 this.radius = radius; this.getDiameter = function () { return 2 * this.radius; }; } const circle1 = new Circle(5); const circle2 = new Circle(10); console.log(circle1.getDiameter()); // 10 console.log(circle2.getDiameter()); // 20
--> 생성자 함수는 객체 (인스턴스) 를 생성하지 않으면 일반 함수로 동작!!
4. Function.prototype.apply / call / bind 메서드에 의한 간접 호출
--> 이들 함수는 모든 함수가 상속받아 사용이 가능하다.
1) apply, call 메서드는 this로 사용할 객체와 인수 리스트를 인수로 전달받아 함수를 호출function getThisBinding() { return this; } // this로 사용할 객체 const thisArg = { a: 1 }; console.log(getThisBinding()); // window // 함수 호출 + 인수로 전달한 객체를 함수의 this에 바인딩 console.log(getThisBinding.apply(thisArg)); // { a : 1 } console.log(getThisBinding.call(thisArg)); // { a : 1 }
* apply, call 메서드의 본질적인 기능은 함수를 호출하는 것
apply vs call
* apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달
* call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달
--> 함수를 호출하는 것은 동일하다function getThisBinding() { console.log(arguments); return this; } // this로 사용할 객체 const thisArg = { a : 1 }; // apply console.log(getThisBinding.apply(thisArg, [1,2,3])); // --> thisArg가 this의 역할, 1,2,3 배열로 묶어 인수 전달 //call console.log(getThisBinding.call(thisArg, 1, 2, 3)); // --> thisArg가 this의 역할, 1,2,3 쉼표 리스트로 인수 전달 // 두 메서드 모두 함수를 호출하므로 결과값은 { a : 1 } 이다.
2) bind 메서드는 함수를 호출하지 않고 this로 사용할 객체만 전달
function getThisBinding() { return this; } // this로 사용할 객체 const thisArg = { a : 1 }; // bind 메서드는 함수에 this로 사용할 객체 전달, 호출은 x console.log(getThisBinding.bind(thisArg)); // getThisBinding // 명시적 호출이 필요하다. console.log(getThisBinding.bind(thisArg)()); // { a : 1 }
--> 메서드의 this와 메서드 내부의 중첩 함수, 콜백 함수의 this가 불일치하는 문제 해결
const person = { name: 'Lee', foo(callback) { setTimeout(callback.bind(this), 100); } }; person.foo(function () { console.log(`Hi! my name is ${this.name}.`); }); // 일반 함수 내부의 this가 전역이 아닌 person을 가리킨다.
일반 함수 호출 --> 전역 객체에 바인딩
메서드 호출 --> 메서드를 호출한 객체에 바인딩
생성자 함수 --> 생성자 함수가 생성할 인스턴스에 바인딩
Function.prototype.apply/call/bind --> 메서드에 첫 번째 인수로 전달한 객체