목차
💻오늘 배운 내용
this
객체지향 언어에서의 this는 곧 클래스로 생성한 인스턴스
💡 비슷한 성질을 가진 여러개의 객체를 만들기 위해, 일종의 설계도라고 할 수 있는
생성자 함수(Constructor)를 만들어 찍어내듯 사용하는데 이렇게 생성된 객체를 인스턴스
생성자 함수(Constructor) = 거푸집
인스턴스 = 거푸집으로 찍어낸 칼
this는 실행 컨텍스트가 생성될 때 결정된다. 즉, bind 한다.
this는 함수를 호출할 때 결정된다.
1] 전역 공간에서의 this
전역객체를 가리킨다
런타임 환경에 따라 this는 window(브라우저 환경) 또는 global(node 환경)
2] 메소드 내부에서의 this
함수와 메소드의 차이는 독립성
// CASE1 : 함수
// 호출 주체를 명시할 수 없기 때문에 this는 전역 객체를 의미
var func = function (x) {
console.log(this, x);
};
func(1); // Window { ... } 1
// CASE2 : 메소드
// 호출 주체를 명시할 수 있기 때문에 this는 해당 객체(obj)를 의미
// obj는 곧 **{ method: f }**
var obj = {
method: func,
};
obj.method(2); // { method: f } 2
함수로서의 호출과 메서드로서의 호출 구분 기준 : . []
3] 함수 내부에서의 this
어떤 함수를 함수로서 호출할 경우, this는 지정되지 않는다 ← 호출 주체를 알 수 없기 때문
실행컨텍스트를 활성화할 당시 this가 지정되지 않은 경우, this는 전역 객체를 의미
함수로서 ‘독립적으로’ 호출할 때는 this는 항상 전역객체를 가리킨다
‼️ 메서드의 내부라고 해도, 함수로서 호출한다면 this는 전역 객체를 의미
var obj1 = {
outer: function() {
console.log(this); // (1) obj1
var innerFunc = function() {
console.log(this); // (2) 전역객체, (3) obj2
}
innerFunc();
var obj2 = {
innerMethod: innerFunc
};
obj2.innerMethod();
}
};
obj1.outer();
💡 this 바인딩에 관해서는 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지가 관건!
4] 콜백함수 내부에서의 this
콜백함수? “어떠한 함수, 메서드의 인자(매개변수)로 넘겨주는 함수”
콜백 함수에 별도로 this를 지정한 경우는 예외적으로 그 대상을 참조하게 되어있다.
// 별도 지정 없음 : 전역객체
setTimeout(function () { console.log(this) }, 300);
// 별도 지정 없음 : 전역객체
[1, 2, 3, 4, 5].forEach(function(x) {
console.log(this, x);
});
// addListener 안에서의 this는 항상 호출한 주체의 element를 return하도록 설계되었음
// 따라서 this는 button을 의미함
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function(e) {
console.log(this, e);
});
- setTimeout 함수, forEach 메서드는 콜백 함수를 호출할 때 대상이 될 this를 지정하지 않으므로, this는 곧 window객체
- addEventListner 메서드는 콜백 함수 호출 시, 자신의 this를 상속하므로, this는 addEventListner의 앞부분(button 태그)
this 우회방법
- 변수를 활용하는 방법
- 내부 스코프에 이미 존재하는 this를 별도의 변수(ex : self)에 할당하는 방법
- 화살표 함수를 활용하는 방법따라서, this는 이전의 값-상위값-이 유지
- 일반 함수와 화살표 함수의 가장 큰 차이점은 무엇? this binding 여부가 가장 적절한 답
- 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 없다.
명시적 this 바인딩
1. call 메소드
호출 주체인 함수를 즉시 실행하는 명령어
call명령어를 사용하여, 첫 번째 매개변수에 this로 binding할 객체를 넣어주면 명시적으로 binding
var func = function (a, b, c) { console.log(this, a, b, c); };
// no binding
func(1, 2, 3); // Window{ ... } 1 2 3
// 명시적 binding
// func 안에 this에는 {x: 1}이 binding
func.call({ x: 1 }, 4, 5, 6}; // { x: 1 } 4 5 6
2. apply 메소드
call 메소드와 완전히 동일
단, this에 binding할 객체는 똑같이 넣어주고 나머지 부분만 배열 형태로 넘겨줌
var func = function (a, b, c) {
console.log(this, a, b, c);
};
func.apply({ x: 1 }, [4, 5, 6]); // { x: 1 } 4 5 6
var obj = {
a: 1,
method: function (x, y) {
console.log(this.a, x, y);
}
};
obj.method.apply({ a: 4 }, [5, 6]); // 4 5 6
3. call / apply 활용
- 유사배열객체(array-like-object)에 배열 메서드를 적용.
객체에는 배열 메소드를 직접 적용할 수 없다.
call 또는 apply 메소드를 이용하여 배열 메소드를 차용
- 생성자 내부에서 다른 생성자를 호출 (공통된 내용의 반복 제거)
- 여러 인수를 묶어 하나의 배열로 전달할 때 apply 사용
4. 바인드 메소드
call과는 다르게 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하는 메소드
함수에 this를 미리 적용 부분 적용
함수 구현할 때 용이
- bind 메서드를 적용해서 새로 만든 함수는 name 프로퍼티에 ‘bound’ 라는 접두어가 붙는다
var func = function (a, b, c, d) {
console.log(this, a, b, c, d);
};
var bindFunc = func.bind({ x:1 }, 4, 5);
// func와 bindFunc의 name 프로퍼티의 차이를 살펴보세요!
console.log([func.name](<http://func.name/>)); // func
console.log([bindFunc.name](<http://bindfunc.name/>)); // bound func
- 상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기
5. 화살표 함수의 예외사항
함수 내부에는 this의 할당과정(바인딩 과정)이 아에 없으며, 접근코자 하면
스코프체인상 가장 가까운 this에 접근하게 됨
this우회, call, apply, bind보다 편리한 방법
❓발생한 이슈/고민
// 가장 아래의 코드가 실행 되었을 때,
// “Passed ~” 가 출력되도록 getAge 함수를 채워주세요
var user = {
name: 'john',
age: 20,
};
var getAged = function (user, passedTime) {
// 여기를 작성해 주세요!
};
var agedUser = getAged(user, 6);
var agedUserMustBeDifferentFromUser = function (user1, user2) {
if (!user2) {
console.log("Failed! user2 doesn't exist!");
} else if (user1 !== user2) {
console.log(
'Passed! If you become older, you will be different from you in the past!'
);
} else {
console.log('Failed! User same with past one');
}
};
agedUserMustBeDifferentFromUser(user, agedUser);
나이든유저라는 문제다.
getAged fuction을 완성하여 ‘Passed! ~~’를 반환하면 되는 것 같다.
오늘 배운 것을 활용하여 풀어볼 수 있을까?
💡해결과정
보자마자 들었던 생각은 유저객체를 배열처럼 요소 하나하나 돌리는 forEach문을 이용해서
두번째 요소인([1]) age에 접근해서 passedTime을 더해주는 생성자를 만들면 되지 않을까?
user.forEach.call(() => user[1] + passedTime);
user.forEach.call(() => user[1] + passedTime); ^
이러한 오류메세지가 뜨는것을 보면 call을 쓰는게 아니라는 것 같다
간단하다고 생각했는데 이거 … 내가 뭘 몰라서 쉬워보이는 것일지도.
var User = {
name: 'john'
age: 30
};
var user = Array.from(User);
var getAged = function (user, passedTime) {
user.forEach.call(() => user[1] + passedTime);
};
forEach는 그대로 두고, user를 Array.from으로 배열로 만든 다음 적용해봤는데
Failed! user2 doesn't exist! <<값이 없다고 한다
agedUser를 콘솔로 찍어보니 undefined가 나오는데 애초에 함수를 잘 못 작성한 것 같다.
var getAged = function (user, passedTime) {
Array.prototype.slice.call(user);
user[1] = user[1] + passedTime;
};
배열로 바꾸고 value 부분을 빼낸다음 passedtime을 더해보다가 깨달았다.
그러고 보니 저건 유사배열이 아니잖아.. 유사배열객체에는 반드시 length 값이 있어야 했다!
신나서 객체에 length: 2를 추가하고 다시 확인해봤는데 undefined 뜨는걸 보면 이것도 아닌가보다.
처음부터 다시 하는 마음으로 지금까지의 고민들 싹 지우고 다시
var getAged = function (user, passedTime) {
user.call(this, user.name);
this.age = age + passedTime;
};
생성자 내부에서 다른 생성자를 호출하는 방법으로 생각해봤는데
user.call(this, user.name);
^
콜이 아니랜다…
오늘도 내 힘으로 풀지 못하고 해설을 보러갔다.
이 문제의 목적은 객체에 대한 복사를 할 수 있는지를 물어보는 문제였다고 한다.. 헐…
user와 agedUser를 비해서 상황에 따른 메세지를 확인하는 것!
1. 새로운 어떤 유저를 복사해서 (newUser)
2. newUser.age를 +=passedTime 해준다면!?
>> 삐빅! failed가 나온다. user1과 user2가 같다고 반환하기 때문
객체는 참조형 변수이기 때문에 계속 변화하는게 원인
1. for ~ in문을 활용해서 user를 newUser로 복사한 뒤,
2. newUser의 age만 passedTime을 더해준다.
답안이다
var user = {
name: "john",
age: 20,
}
// 객체 만들어 프로퍼티 복사하기
var getAged = function (user, passedTime) {
var result = {};
for (var prop in user) {
result[prop] = user[prop];
}
result.age += passedTime;
return result;
}
var agedUser = getAged(user, 6);
var agedUserMustBeDifferentFromUser = function (user1, user2) {
if (user1 !== user2) {
console.log("Passed! If you become older, you will be different from you in the past!")
} else {
console.log("Failed! User same with past one");
}
}
agedUserMustBeDifferentFromUser(user, agedUser);
스스로 여기까지 생각 해보고 싶다..
이번에는 아주 방향부터 잘못잡은 것 같다.
🧐궁금점과 부족한 내용
예제에서 ‘전역객체를 받는다’와 ‘outer를 받는다’는 표현의 차이가 무엇인지?
다른 예제를 가져와서 비교해봐야할듯
전역객체를 받는다 = this가 유실된다 같은 말인가?
apply 안쓰면 왜 NaN이 되지?
//효율
var numbers = [10, 20, 3, 16, 45];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max, min); // 45 3
다음주에 튜터님한테 가서 질문해봐야겠다 총총…
'내일배움캠프 > Today I Learned' 카테고리의 다른 글
[TIL 2023.05.30] 자바스크립트 검색기능 (GOE’s CINEMA) (0) | 2023.05.31 |
---|---|
[TIL 2023.05.29] 영화 검색 페이지 만들기 (GOE’s CINEMA) (0) | 2023.05.30 |
[TIL 2023.05.25] JavaScript 데이터 타입, 실행컨텍스트 (1) | 2023.05.26 |
[TIL 2023.05.24] ES6, 일급객체로서의 함수, Map과 Set (1) | 2023.05.25 |
[TIL 2023.05.23] TIL 쓰는법, JavaScript 조건문, 비교문 (0) | 2023.05.24 |