[CoreJavascript] Chapter 03. this 내용 정리
Chapter 03 - this
3-1 상황에 따라 달라지는 this
→ this는 함수를 호출할 때 결정된다.
→ 함수를 어떤 방식으로 호출하느냐에 따라 값이 달라지는 것
3-1-1 전역 공간에서의 this
전역 공간에서 this는 전역 객체를 가르킨다.
전역 객체는 자바스크립트 런타임 환경에 따라 이름과 정보를 가짐.
- 전역객체 : 전역 컨텍스트 생성 주체(브라우저 : window , node.js : global)(참고) 자바스크립트의 모든 변수는 실은 특정 객체의 프로퍼티(실행 컨텍스트의 LexicalEnvironment) 로서 동작한다.
전역변수와 젼역객체(1)
var a = 1;
console.log(a); // 1
console.log(window.a) // 1
console.log(this.a) // 1
→ 자바스크립트의 모든 변수는 특정 객체의 프로퍼티로 동작
- 사용자가 var 연산자를 이용해 변수를 선언하더라도 실제 자바스크립트 엔진은 어떤 특정 객체의 프로퍼티로 인식한다.
전역변수와 전역객체 선언
var a = 1;
window.b = 2;
console.log(a, window.a, this.a); // 1 1 1
console.log(b, window.b, this.b); // 2 2 2
→ 전역 공간에서는 var로 변수 선언하는 대신 window의 프로퍼티에 직접 할당 가능
전역변수와 전역객체 삭제
var a = 1;
delete window.a; // false
console.log(a, window.a, this.a) // 1 1 1
var b = 2;
delete b // false
console.log(b, window.b, this.b) // 2 2 2
window.c = 3;
delete window.c; // true
console.log(c, window.c, this.c); // Uncaught ReferenceError: c is not defined
window.d = 4;
delete d; // true
console.log(d, window.d this.d); // Uncaught ReferenceError: d is not defined
→ delete 연산자 앞에는 (window.) 생략
→ 전역객체의 프로퍼티로 할당한 경우엔 삭제 가능 그러나 전역변수로 선언한 경우에는 삭제가 되지 않음
→ 전역변수를 선언하면 자바스크립트 엔진이 이를 자동으로 전역객체의 프로퍼티로 할당하면서 추가적으로 해당 프로퍼티의 configurable 속성 (변경 및 삭제 가능성) 을 false 정의
3-1-2 메서드로서 호출할 때 그 메서드 내부에서의 this
함수 Vs 메서드
→ 함수 실행 방법은 두가지 존재 : 함수로서 호출 Or 메서드로서 호출
→ 둘의 차이점은 독립성
→ 함수는 그 자체로 독립적인 기능 수행
→ 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행
함수로서 호출, 메서드로서 호출
var func = function (x) {
console.log(this, x);
};
func(1); // Window { ... } 1
var obj = {
method: func
};
obj.method(2); // { method: f } 2
→ 1번 func라는 변수에 익명함수는 4번째 줄에서 func 호출시 this로 전역객체 window 출력.
→ 2번째 obj변수는, method 호출시 this가 obj 이다.
** 즉 원래의 익명함수는 그대로이지만 이를 변수에 담아 호출 시 obj 객체는 프로퍼티에 할당해서 호출한 경우 this가 달라짐.
?? ‘함수로서 호출’과 ‘메서드로서 호출’ 구분 방법
→ 함수 앞에 점(.)이 있는지 여부로 간단히 구분 가능.
메서드로서 호출 - 점 표기법, 대괄호 표기법
var obj = {
method : function (x) { console.log(this, x); }
};
obj.method(1); // { method: f } 1
obj['method'](2); // { method: f } 2
메서드 내부에서의 this
this에서는 호출한 주체에 대한 정보가 담김.
→ 어떤 함수를 메서드로서 호출한 경우 호출 주체는 바로 함수명(프로퍼티명) 앞의 객체.
→ 점 표기법의 경우 마지막 점 앞에 명시된 객체가 곧 this가 되는 것
3-1-3 함수로서 호출할 때 그 함수 내부에서의 this
함수 내부에서의 this. 함수를 함수로서 호출한 경우에는 호출 주체의 정보를 알 수 없기 때문에 this가 지정되지 않았기 때문에 this는 전역 객체를 가리킨다.
메서드의 내부 함수에서의 this
내부 함수에서의 this
var obj = {
outer: function() {
console.log(this); // obj1
var innerFunc = function (){
console.log(this); // window
}
innerFunc(); // 함수로서 호출
var obj2 = {
innerMethod: innerFunc // obj2
};
obj2.innerMethod(); // 메서드로서 호출
}
};
obj1.outer(); // 메서드로서 호출
메서드의 내부 함수에서의 this를 우회하는 방법
내부 함수에서의 this를 우회하는 방법
var obj = {
outer: function() {
console.log(this); // {outer: f}
var innerFunc1 = function (){
console.log(this); // window
}
};
innerFunc1();
var self = this;
var innerFunc2 = function() {
console.log(self); // {outer: f}
};
};
obj.outer();
this를 바인딩 하지 않는 함수
this를 바인딩 하지 않는 화살표 함수(arrow function) 도입 (화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this를 그대로 활용 가능)
3-1-4 콜백 함수 호출 시 그 함수 내부에서의 this
콜백 함수 : 함수 A의 제어권을 다른 함수(또는 메서드) B에게 넘겨주는 경우 제어권을 넘겨준 함수 A
- 함수 A는 함수 B의 내부 로직에 따라 실행
콜백 함수 내부에서의 this
setTImeout(function() { console.lgo(this); }, 300); // (1)
[1,2,3,4,5].forEach(function (x) {
console.log(this, x);
}); // (2)
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a')
.addEventListner('click', function (e) {
console.log(this, e);
}); // (3)
(1) : 0.3초뒤 전역객체 출력
(2): 전역객체와 배열의 각 요소가 총 5회 출력
(3): 버튼 클릭하면 앞서 지정한 엘리먼트와 클릭 이벤트에 관한 정보가 담긴 객체 출력
3-1-5 생성자 함수 내부에서의 this
생성자 함수
: 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수
객체지향 언어 ⇒ 클래스(생성자), 인스턴스(클래스를 통해 만든 객체) ⇒
생성자
: 구체적인 인스턴스를 만들기 위한 틀
사용법 : new 명령어와 함께 함수를 호출 → 생성자의 prototype 프로퍼티를 참조하는 proto라는 프로퍼티가 있는 객체 생성 → 미리 준비된 공통 속성 및 개성을 해당 객체(this)에 부여
생성자 함수
var Cat = function (name, age) {
this.bark = '야옹'
this.name = name;
this.age = age;
};
var choco = new Cat("초코", 7);
var nabi = new Cat("나비", 5);
console.log(choco, nabi);
// Cat{ bark: '야옹', name:'초코', age:7 }
// Cat{ bark: '야옹' name:'나비', age:5 }
3-2 명시적으로 this를 바인딩하는 방법
3-2-1 call 메서드
Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])
call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령어
→ 함수를 그냥 실행하면 this 전역객체를 참조하지만 cal 메서드를 이용하면 임의의 객체를 this로 지정.
call 메서드 예시 (1) - 함수로서 호출시
var func = function (a,b,c) {
console.log(this, a, b, c);
}
func(1,2,3); // window{...} 1 2 3
func.call({ x:1 },4,5,6); // { x: 1 } 4 5 6
call 메서드 예시 (2) - 메서드로서의 호출 시
var obj = {
a : 1,
method: function (x,y) {
console.log(this.a, x, y);
}
};
obj.method(2,3); // 1 2 3
obj.method.call({ a: 4}, 5, 6) // 4 5 6
3-2-2 apply 메서드
function.prototype.apply(thisArg[, argsArray])
→ apply 메서드는 call 메서드와 기능적 완전 동일
- call 메서드와 apply 차이점
- call 메서드는 첫 번째 인자를 제외한 나머지 모든 인자들을 호출할 함수의 매개변수로 지정
- apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정.
apply메서드
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-2-3 call / apply 메서드의 활용
유사배열객체(array-liek object) 에 배열 메서드를 적용
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
Array.prototype.push.call(obj,'d');
console.log(obj); // { 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }
var arr = Array.prototype.slice.call(obj);
console.log(arr) // [ 'a', 'b', 'c', 'd' ]
유사 배열 객체 : 키가 0 또는 양의 정수인 프로퍼티가 존재하고 length 프로퍼티의 값이 0 또는 양의 정수인 객체 (배열의 구조와 유사한 객체의 경우)
arguments, NodeList에 배열 메서드 적용
function a () {
var argv = Array.prototype,slice.call(arguments);
agrv.forEach( function (arg) {
console.log(arg);
});
}
a(1,2,3);
document.body.innerHTML = '<div>a</div><div>b</div><div>c</div>
var nodeList = document.querySelectAll('div');
var nodeArr = Array.prototype.slice.call(nodeList);
nodeArr.forEach(function (node)) {
console.log(node);
}
ES6의 Array.from 메서드 적용
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
var arr = Array.from(obj);
console.log(arr); // [ 'a', 'b', 'c' ];
생성자 내부에서 다른 생성자를 호출
생성자 내부에 다른 생성자와 공통된 내용이 있을 경우 call 또는 apply를 이용해 다른 생성자를 호출하여 간단하게 반복을 줄일 수 있게 함.
생성자 내부에서 다른 생성자를 호출
function Person(name, gender) {
this.name = name;
this.gender = gender;
}
function Student(name,gender,school) {
Person.call(this,name,gender);
this.school = school;
}
function Employee(name,gender, compaany) {
person.apply(this, [name,gender]);
this.company = company;
}
var by = new Stduent('보영', 'female', '단국대');
var jn = new Employee('재난', 'male', '구골');
ES6의 펼치기 연산자 활용
const numbers = [10, 20, 3, 16, 45];
const max = Math.max(...numbers);
const min = Math.min(...numbers);
console.log(max, min); // 45 3
3-2-4 bind 메서드
Function.prototype.bind(thisArg[, arg1[, ag2[, ...]]])
call과 비슷하지만 즉시 호출하지 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드
→ 즉 bind 메서드는 함수에 this를 미리 적용한 것과 부분 적용 함수를 구현하는 두가지 목적을 지님.
bind 메서드 - this 지정과 부분 적용 함수 구현
var func = function (a, b, c, d){
console.log(this, a, b, c,d);
};
func(1, 2, 3, 4); // window{ ... } 1 2 3 4
var bindFunc1 = func.bind({x: 1});
bindFunc1(5, 6, 7, 7); // { x: 1 } 5 6 7 8
var bindFunc2 = func.bind({ x: 1 }, 4, 5);
bindFunc2(6, 7); // { x: 1 } 4 5 6 7
bindFunc2(8, 9); // { x: 1 } 4 5 8 9
name 프로퍼티
name 프로퍼티에 동사 bind의 수동태인 ‘bound’ 라는 접두어가 붙는다
var func = function (a, b, c, d){
console.log(this, a, b, c,d);
};
var bindFunc = func.bind({ x: 1 }, 4, 5);
console.log(func.name); // func
console.log(bindFunc.name); // bound func
상위 컨텍스트의 this를 내부 함수나 콜백 함수에 전달하기
메서드의 내부 함수에서 메서드의 this를 그대로 바라보게 하기 위한 방법으로 self를 사용하는 것보다 call, apply, bind 메서드를 이용해 더 깔끔하게 처리 가능
상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기
메서드의 내부 함수에서 메서드의 this를 그대로 바라보게 하기 위한 방법으로 self를 사용하는 것보다 call, apply, bind 메서드를 이용해 더 깔끔하게 처리 함.
3-2-5 화살표 함수의 예외사항
화살표 함수는 실행 컨텍스트 생성 시 this를 바인딩하는 과정이 제외되는데, 이 함수 내부에는 this가 아예 없으며, 접근하고자 하면 스코프체인상 가장 가까운 this에 접근
화살표 함수 내부에서의 this
var obj = {
outer : function () {
console.log(this);
var innerFunc = () => {
console.log(this);
};
innerFunc();
}
};
obj.outer();
→ 내부함수를 화살표 함수로 바꿈.
→ 별도의 변수로 this 우회하거나 call/ apply / bind를 적용 할 필요 x
3-2-6 별도의 인자를 this를 받는 경우 (콜백 함수 내에서의 this)
콜백 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체(thisArg)를 인자로 지정할 수 있는 경우가 있는데, 이러한 메서드의 thisArg 값을 지정하면 콜백 함수 내부에서 this 값을 원하는 대로 변경 가능.
콜백 함수와 함께 thisArg를 인자로 받는 메서드
Array.prototype.forEach(callback[, thisArg])
Array.prototype.map(callback[, thisArg])
Array.prototype.filter(callback[, thisArg])
Array.prototype.some(callback[, thisArg])
Array.prototype.every(callback[, thisArg])
Array.prototype.find(callback[, thisArg])
Array.prototype.findIndex(callback[, thisArg])
Array.prototype.flatMap(callback[, thisArg])
Array.prototype.from(arrayLike[, callback[, thisArg]])
Set.prototype.forEach(callback[, thisArg])
Map.prototype.forEach(callback[, thisArg])