-
JavaScript - 메서드와 this개발언어/JavaScript 2022. 12. 22. 17:25
메서드와 this
자바스크립트에선 객체의 프로퍼티에 함수를 할당해 객체에게 행동할 수 있는 능력을 부여한다.
메서드 만들기
let user = { name: "John", age:30 }; user.sayHi = function(){ alert("안녕하세요!"); } user.sayHi(); //안녕하세요!
메서드는 아래와 같이 이미 저의된 함수를 이용해서 만들수도 있다.
let user = { //... } //함수선언 function sayHi(){ alert("안녕하세요"); }; // 선언된 함수를 메서드로 등록 user.sayHi = sayHi; user.sayHi(); //안녕하세요
메서드 단축 구문
객체 리터럴 안에 메서드를 선언할 때 사용할 수 있는 단축 문법
//아래 두 객체는 동일하게 동작한다. user = { sayHi: function(){ alert("Hello"); } }; //단축 구문을 사용 user = { sayHi(){ // "sayHi: function()"과 동일하다. alert("Hello"); } };
하지만 일반적인 방법과 단축 구문을 사용한 방법이 완전히 동일하지는 않다. 객체 상속과 관련된 미묘한 차이가 존재한다.
메서드와 this
메서드는 객체에 저장된 정보에 접근할 수 있어야 제 역할을 할 수 있다. 모든 메서드가 그런건 아니지만, 대부분의 메서드가 객체 프로퍼티 값을 활용한다.
user.asyHi()의 내부 코드에서 객체 user에 저장된 이름(name)을 이용해 인사말을 만드는 경우가 이런 경우에 속한다. 메서드 내부에서 this키워드를 사용하면 객체에 접근할 수 있다.
이때 '점 앞'의 this 는 객체를 나타냅니다. 정확히는 메서드를 호출할 때 사용된 객체를 나타낸다.
user = { name: "John", age: 30, sayH(){ // 'this'는 '현재 객체'를 나타낸다. alert(this.name); } }; user.sayHi(); //John
user.sayHi()가 실행되는 동안에 this는 user를 나타낸다.
this를 사용하지 않고 외부 변수를 참조해 객체에 접근하는 것도 가능하다.
user = { name: "John", age: 30, sayH(){ alert(user.name); //'this' 대신 'user'를 이용한다. } };
그런데 이렇게 외부 변수를 사용해 객체를 참조하면 예상치 못한 에러가 발생할 수 있다. => user를 복사해 다른 변수에 할당(admin = user)하고, user는 전혀 다른 값으로 덮어썼다고 가정해 보면, sayHi()는 원치 않는 값(null)을 참조할 것이다.
user = { name: "John", age: 30, sayH(){ alert(user.name); //Error: Cannot read property 'name' of null } }; let admin = user; user = null; // user를 null로 덮어쓴다. admin.sayHi(); // sayHi()가 엉뚱한 객체를 참고하면서 에러가 발생했습니다.
alert 함수가 user.name 대신 this.name을 인수로 받았다면 에러가 발생하지 않았을 것이다.
자유로운 this
자바스크립트의 this는 다른 프로그래밍 언어의 this와 동작 방식이 다르다. 자바스크립트에서는 모든 함수에 this를 사용할 수 있다.
아래와 같이 코드를 작성해도 문법 에러가 발생하지 않는다.
function sayHi(){ alert(this.name); }
this 값은 런타임에 결정된다. 컨텍스트에 따라 달라진다.
동일한 함수라도 다른 객체에서 호출했다면 'this'가 참조하는 값이 달라진다.
let user = { name: "John" }; let admin = { name: "Admin" }; function sayHi(){ alert(this.name); } //별개의 객체에서 동일한 함수를 사용한다. user.f = sayHi; admin.f = sayHi; //'this'는 '점(.) 앞의' 객체를 참조하기 때문에 //this 값이 달라진다. user.f(); //John (this == user) admin.f(); //Admin (this == admin) admin['f'](); //Admin (점과 대괄호는 동일하게 동작한다.)
규칙은 간단하다 obj.f()를 호출하면 this는 f를 호출하는 동안의 obj이다.
객체 없이 호출하기 : this == undefined
객체가 없어도 함수를 호출할 수 있다.
function sayHi(){ alert(this); } sayHi(); // undefined
위와 같은 코드를 "use strict"에서 실행하면 , this에는 undefined가 할당된다. this.name으로 name에 접근하려고 하면 에러가 발생한다.
그런데 "use strict"를 사용하지 않을 땐 this가 전역 객체를 참조한다. 브라우저 환경에서는 window라는 전역 객체를 참조한다. 이런 동작의 차이는 "use strict"가 도입된 배경이기도 하다.
함수 본문에 this가 사용되었다면, 객체 컨텍스트 내에서 함수를 호출할 것이라고 예상하면 된다.
자유로운 this가 만드는 결과
다른 언어를 사용해본 개발자는 자바스크립트의 this개념에 혼선을 겪기 쉽다. 즉 this가 항상 정의된 객체를 참조할 것이라고 착각한다.
이런 개념을 'bound this'라고 한다.
자바스크립트에서 this는 런타임에 결정된다. 메서드가 어디서 정의되었는지 상관없이 this는 '점 앞의' 객체가 무엇인가에 따라 '자유롭게' 결정된다.
이렇게 this가 런타임에 결정되면 함수(메서드)를 하나 만들어서 여러 객체에 재사용할 수 있다는 장점이 있짐나 이런 유연함이 실수로 이러질 수 있다는 것은 단점이다.
this가 없는 화살표 함수
화살표 함수는 일반 함수와는 달리'고유한'this를 가지지 않는다. 화살표 함수에서 this를 참조하면 화살표 함수가 아닌 '평범한' 외부 함수에서 this 값을 가져온다.
아래 예시에서 함수 arrow() this는 외부 함수 user.sayHi()의 this가 된다.
let user = { firstName: "보라", sayHi(){ let arrow = () => alert(this.firstName); arrow(); } }; user.sayHi(); //보라
별개의 this가 만들어 지는 건 원하지 않고, 외부 컨텍스트에 있는 this를 이용하고 싶은 경우 화살표 함수가 유용하다.
객체 리터럴에서 'this' 사용하기
함수 makeUser는 객체를 반환한다.
function makeUser(){ return { name:"John", ref:this }; }; let user = makeUser(); alert(user.ref.name); // Error: Cannot read property 'name' of undefined
에러가 발생하는 이유는 this값을 설정할 땐 객체 정의된 특정 대상 객체가 없기 때문이다. 따라서 this값은 호출 시점에 결정된다. 위 코드에서 makeUser() 안의 this는 undefined가 된다. 왜냐하면 어떤 특정 객체의 멤버 메서드로 호출된게 아니라 그냥 독립적인 함수로써 호출이 되었기 때문입니다.
따라서 this 값은 함수 전체가 되고 즉 this가 코드블록인 객체리터럴로 한정되지 않는 것이다. 그래서 ref: this는 함수의 현재 this 값을 가져오는데 해당 함수는 어떤 객체에 할당되지 않은채 정의 되고 추후에도 특정 객체 할당되지 않고 호출되므로 this 값은 undefined가 된다.
function makeUser(){ return this; //이번엔 객체 리터럴을 사용하지 않습니다. } alert(makeUser().name); // Error: Cannot read property 'name' of undefined
보다시피 alert(makeUser().name)와 위쪽에서 살펴본 alert(user.ref.name)의 결과가 같은 것을 확인할 수 있다.
에러가 발생하지 않게 하려면 코드를 다음과 같이 수정하면 된다.
function makeUser(){ return{ name: "John", ref(){ retun this; } }; }; let user = makeUser(); alert(user.ref().name); //John
위와 같이 코드를 작성하면 user.ref()가 메서드가 되고 user가 "." this의 객체가 되기 때문에 에러가 발생하지 않는다.
계산기 만들기
let calculator = { num1, num2, read: () => { this.num1 = prompt("좌항의 값을 입력하시오"); this.num2 = prompt("우항의 값을 입력하시오"); }, sum: () => { return this.num1 + this.num2; }, mal: () => { return this.num1 * this.num2; }, }; calculator.read(); alert(calculator.sum()); alert(calculator.mal());
let calculator = { sum() { return this.a + this.b; }, mul() { return this.a * this.b; }, read() { this.a = +prompt('첫 번째 값:', 0); this.b = +prompt('두 번째 값:', 0); } }; calculator.read(); alert( calculator.sum() ); alert( calculator.mul() );
정리
this 값은 런타임에 결정된다.
- 함수를 선언할 때 this를 사용할 수 있다. 다만, 함수가 호출되기 전까지 this엔 값이 할당되지 않는다.
- 함수를 복사해 객체간 전달 할 수있다.
- 함수를 객체 프로퍼티에 저장해 object.method()같이 '메서드' 형태로 호출하면 this는 object를 참조한다.
화살표 함수는 자신만의 this를 가지지 않는다는 점에서 독특하다. 화살표 함수 안에서 this를 사용하면, 외부에서 this 값을 가져온다.
'개발언어 > JavaScript' 카테고리의 다른 글
JavaScript - 프로토타입 (0) 2022.12.23 JavaScript - 함수 바인딩 (0) 2022.12.23 JavaScript - 객체 (0) 2022.12.22 async와 await 그리고 유용한 Promise APIs (0) 2022.06.10 Promise 개념부터 활용 (0) 2022.06.09