개요
- this의 정의
- 함수호출방식과 this 바인딩
this의 정의
Javascript의 예약어로써 함수의 호출 방식에 따라 this에 바인딩되는 객체가 달라진다.
- 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수
- 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다
- Javascript엔진에 의해 암묵적으로 생성 ( + 호출시 내부에 인자와 this가 전달된다)
- 일반적으로 객체의 메서드 내부 또는 생성자 함수 내부에서만 의미가 있다. (참조 변수이기때문에)
- 지역변수처럼 사용 가능
느낌상 객체 내부에서 사용하는 것을 많이 떠올리게 된다. value라는 값을 가지고 있는 객체에 foo라는 함수를 할당하려할 때 그 함수에서 value를 사용하려 할 때 this.value 이런 느낌
바인딩)
식별자와 값을 연결하는 과정으로 변수의 경우 이름과 확보된 메모리 공간의 주소를 바인딩한다.
this바인딩은 this가 가리킬 개게를 바인딩하는 것
함수호출방식과 this바인딩
함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아닌 호출 시에 동적으로 결정된다
렉시컬스코프와 혼동하지 않도록 유의하자
호출방식)
- 함수호출
- 메소드 호출
- 생성자 함수 호출
- apply / call / bind 호출
1. 함수 호출)
전역객체(Global Object)란 모든 객체의 유일한 최상위 객체이며 Browser에서는 window, Node.js에서는 global객체를 의미한다.
- 호출단계에서 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();
- 일반 함수, 메소드, 콜백함수 어디든 사용된 내부함수의 this는 전역객체를 바인딩한다.
- 설계단계의 결함으로 this의 원래 역할을 제대로 수행할 수 없다 (내부함수를 사용하여 자신의 작업을 돕게 할 수 없음)
회피방법)
- 변수를 선언하여 this를 할당하는 방법
- 명시적으로 바인딩할 수 있는 apply / call / bind 메소드 이용
var value = 1;
// this를 변수에 할당
var obj = {
value: 100,
foo: function() {
var that = this; // Workaround : this === obj
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
console.log("bar's that: ", that); // obj
console.log("bar's that.value: ", that.value); // 100
}
bar();
}
};
obj.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(a, b) {
console.log("bar's this: ", this); // obj
console.log("bar's this.value: ", this.value); // 100
console.log("bar's arguments: ", arguments);
}
bar.apply(obj, [1, 2]);
bar.call(obj, 1, 2);
bar.bind(obj)(1, 2);
}
};
obj.foo();
2. 메소드 호출)
함수가 객체의 프로퍼티 값이라면 메소드로 호출되며 이떄 this는 해당 메소드를 소유한 객체에 바인딩된다.
var obj1 = {
name: 'Lee',
sayName: function() {
console.log(this.name);
}
}
var obj2 = {
name: 'Kim'
}
obj2.sayName = obj1.sayName;
obj1.sayName();
obj2.sayName();
3. 생성자 함수 호출)
JAVA와 같은 객체지향언어와는 다르게 Javascript는 기존함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 작동한다
// 생성자 함수
function Person(name) {
this.name = name;
}
var me = new Person('Lee');
console.log(me); // Person {name: "Lee"}
// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person('Kim');
console.log(you); // undefined
생성자 함수 동작 방식)
- 빈 객체 생성 및 this바인딩
- 생성자 함수 코드가 실행하기 전에 빈 객체를 생성해서 생성자 함수 내부에서 사용되는 this가 이 빈 객체를 바인딩하게 한다.
- this를 통한 프로퍼티 생성
- 이제 this.~~~ 이렇게 붙은 애들이 전부 객체의 프로퍼티로 들어간다.
- 생성된 객체 반환
- return 있으면 안된다 그러면 우리가 이제껏 만들었던 객체를 반환하는게 아니라 return을 반환하고 이렇게 되면 생성자 역할을 하지 못하는 함수이기에 return은 사용하면 안됨. 안 쓰면 명시적으로 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를 붙이지 않고 호출한 경우)
this는 일반함수를 호출하면 전역객체를 바인딩하고 생성자 함수를 호출하면 빈 객체에 바인딩되는데 이러한 바인딩 방식이 다르기 때무에 new가 없는 생성자 함수나 new가 있는 일반함수는 오류가 발생할 수 있다.
function Person(name) {
// new없이 호출하는 경우, 전역객체에 name 프로퍼티를 추가
this.name = name;
};
// 일반 함수로서 호출되었기 때문에 객체를 암묵적으로 생성하여 반환하지 않는다.
// 일반 함수의 this는 전역객체를 가리킨다.
var me = Person('Lee');
console.log(me); // undefined
console.log(window.name); // Lee
// window 전역객체임에도 이상한게 나오네 바인딩이 이상하게 됬기 때문
4. apply / call / bind 호출)
this를 명시적으로 바인딩하는 방법으로 모두 Function.prototype 객체의 메소드이다
apply)
var Person = function (name) {
this.name = name;
};
var foo = {};
// apply 메소드는 생성자함수 Person을 호출한다. 이때 this에 객체 foo를 바인딩한다.
Person.apply(foo, ['name']);
console.log(foo); // { name: 'name' }
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
- 위 코드에서 우리는 원하는 값을 도출하지 못했다.
- 이유는 foo()에서 바인딩하고 있는 this는 전역이기에 생성자함수에서의 this와 다른 방향을 보고있기 때문이다.
해결방안)
- call 사용
- bind 사용
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 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'
'CS > 프로그래밍' 카테고리의 다른 글
버블링, 캡쳐링 (0) | 2023.12.07 |
---|---|
클로저 (1) | 2023.12.07 |
Flex 속성(CSS) (0) | 2023.12.01 |
TDD (0) | 2023.11.03 |
원시 값과 참조 값 (0) | 2023.11.03 |