-
[Javascript] this 예약어구버전/JavaScript 2023. 12. 7. 08:56
개요
- 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'
'구버전 > JavaScript' 카테고리의 다른 글
[Javascript] strict mode (0) 2024.01.18 [Javascript] Blocking / Non-Blocking vs Sync / Async (0) 2024.01.17 [CS Study] 화살표 함수와 일반 함수 (0) 2024.01.08 [CS Study] 스코프, 스코프체인 (0) 2024.01.02 [스파르타코딩클럽] Javascript 문법종합반 1~ 2주차 (0) 2023.06.12