코어 자바스크립트 - 03. this

iamsummer__·2021년 1월 7일
0

대부분 객체지향 언어에서 this는 클래스로 생성한 인스턴스 객체를 의미한다.
그러나 자바스크립트에서의 this는 어디서든 사용할수 있으며, 상황에 따라 this가 바라보는 대상이 달라진다.

1️⃣ 상황에 따라 달라지는 this

this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다.
실행 컨텍스트는 함수를 호출할 때 생성되므로,

즉, this는 함수를 호출할 때 결정된다

1) 전역공간에서의 this

전역 공간에서 this는 전역객체를 가르킨다.
브라우저 환경에서 전역객체는 window, Node 환경에서 this는 global이다.

자바스크립트의 모든 변수는 실은 특정 객체의 프로퍼티로서 동작한다.
그러므로 전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다.

변수 a에 접근하고자 하면 스코프 체인에서 a를 검색하다가 가장 마지막에 도달하는 전역 스코프의 LexicalEnvironment 즉, 전역 객체에서 해당 프로퍼티 a를 발견해서 그 값을 반환한다.

console.log(this); // window

var a = 1;
console.log(a); // 1
console.log(window.a); // 1
console.log(this.a); // 1

2) 함수 vs 메서드

함수를 실행하는 방법에는 여러가지가 있는데 대게 일반적으로 함수로서 호출하는 경우
메서드로서 호출하는 경우로 나뉜다.
이 둘의 차이는 독립성이며,

함수는 그 자체로 독립적인 기능을 수행하지만,
메서드는 자신을 호출한 대상 객체 관한 동작을 수행한다.

var func = function(z) {
	console.log(this.z);
}

func(1); // window { ... } 1


var obj = {

	method: func
}

obj.method(2); // {method: f} 2

위의 코드는 함수로서의 호출과 메서드로서의 호출 두 가지 경우를 보여준다.
먼저 함수로서 호출된 func(1)의 경우 this는 전역객체인 window가 된다.
메서드로서 호출된 obj.method(2)의 경우 this는 obj가 된다.

💡 이 두가지를 구분하기 가장 쉬운 방법은??

함수 앞에 점 또는 대괄호 여부이다.
점 표기법이든 대괄호 표기법이든, 어떤 함수를 호출할 때 그 함수 이름 앞에
객체가 명시되어 있는 경우에는 메서드로 호출한 것, 그렇지 않으면 함수로 호출한 것이다.

var obj ={

	methodA: function() { console.log(this)},
    inner: {
    	methodB: function() { console.log(this)}
    }
}

obj.methodA(); // this는 obj
obj['methodA'](); // this는 obj

obj.inner.methodB(); // this는 obj.inner
obj.inner['methodB']; // this는 obj.inner
obj['inner']['methodB']; // this는 obj.inner

3) 메서드 내부 함수에서 this 우회하는 방법

ES5까지는 자체적으로 내부함수에 this를 상속할 방법은 없지만, 우회하는 방법은 있다.
바로 변수를 활용하여 상위 스코프의 this를 저장하는 것이다.
주로 변수명은 self를 많이 사용한다.

var obj = {

	var self = this;
    var inner = function() {
    	console.log(self); // obj
    }
    
    inner();
}

4) 콜백함수 호출 시 함수 내부에서의 this

마찬가지로 this 바인딩을 하지 않으면 전역객체가 this가 된다.
그러나 addEventListener 메서드는 콜백함수를 호출할 때 자신의 this를 상속하게 된다.

setTimeout(function() { console.log(this)}, 100); // window

document.querySelector('#root').addEventListener('click, function() {
	console.log(this); // document.querySelector('#root')
});

5) 생성자 함수 내부에서 this

생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는데 사용하는 함수이다.
객체지향 언어에서는 생성자를 클래스, 클래스를 통해 만든 객체를 인스턴스라고 한다.

생성자는 구체적인 인스턴스를 만들기 위한 일종의 틀이다.

자바스크립트에서는 new 명령어와 함께 함수를 호출하면 해당 함수가 생성자로서 동작하게 된다.
어떤 함수가 생성자 함수로서 호출된 경우 내부에서 this는 곧 새로 만든 구체적인 인스턴스 자신이 된다.

var Cat = function(name,gender) {
	this.family = 'aa'
	this.name= name;
    this.gender = gender;
}


let judy = new Cat('judy', 'female'); // { family: 'aa', name:'judy', gender: 'female'}
let nabi = new Cat('nabi', 'male'); // // { family: 'aa', name:'nabi', gender: 'male'}

실행한 생성자 함수 내부에서 this는 각 인스턴스를 가르킨다.

2️⃣ 명시적으로 this 바인딩하는 방법

1) call 메서드

메서드의 호출주체인 함수를 즉시 실행합니다.
첫번째 인자는 this로 바인딩하고 이후의 인자들은 호출할 함수의 매개변수를 의미합니다.
Function.prototype.call(thisArg[, arg1[, arg2[, ...]]]);

let func = function (a,b,c) {

	console.log(this, a,b,c);
}

func(1,2,3); // window, 1,2,3
 
function.call({x: 1}, 4,5,6);  {x:1}, 4,5,6

2) apply 메서드

call과 기능적으로 완전 동일하지만, 첫번째 인자를 제외한 나머지 인자들을 배열로 받아 매개변수로 지정한다.

let func = function (a,b,c) {

	console.log(this, a,b,c);
}

func(1,2,3); // window, 1,2,3
 
function.apply({x: 1}, [4,5,6]);  {x:1}, 4,5,6

📕 call/apply 메서드를 활용하는 방법

유사배열 객체에 call/apply를 사용하여 배열 메서드를 사용할 수 있습니다.

var obj = {
	0: 'a',
    1: 'b',
    length: 2
}

Array.prototype.push.call(obj, 'd'); 
console.log(obj); // { 0: 'a', 1: 'b', 2: 'd', length: 3);


var arr = Array.prototype.slice.call(obj);
console.log(arr); [a,b,d]

키가 0 또는 양의 정수인 프로퍼티가 존재하며 length 프로퍼티값이 0 또는 양의 정수인 객체,
즉 배열의 구조와 유사한 유사배열 객체는 call/apply 활용하여 배열처럼 사용할 수 있다.
함수 내부에 접근할 수 있는 arguments도 유사배열 객체이므로 사용가능하다.

그러나 call/apply등을 사용하면 딱 코드만 보고서는 어떤 의도인지 파악하기 어렵다.
ES6에 유사배열객체 또는 순회가능한 모든 종류의 데이터 타입을 배열로 전환하는 Array.from 메서드를 사용하면 쉽게 배열로 만들 수 있다.

var obj = {
	0: 'a',
    1: 'b',
    length: 2
}

var arr = Array.from(obj);
console.log(arr); // [a,b]


var nodeList = document.querySelector('#root');
Array.from(nodeList).forEach(function (node) {
	console.log(node);
});
var numbers = [1,2,3,4,5]

var max = Math.max.apply(null, numbers); // 5
var min = Math.min.apply(null, numbers); // 1


var max = Math.max(...numbers); // 5
var min = Math.min(...numbers); // 1

3) 생성자 내부에서 다른 생성자를 호출

생성자 함수 내부에서 person 생성자 함수를 호출해서 인스턴스의 속성을 정의한다.

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, company) {
	Person.call(this,name,gender);
    
    this.company = company;
}

var by = new Student('bbangdi', 'female', '서운대');
var jn = new Employee('juzz', 'female', '코코아');

3) bind 메서드

call과 비슷하지만 즉시 호출하지 않고 넘겨받은 this및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드이다.

bind메서드는 함수에 this를 미리 적용하는 것부분적용함수를 구현하는 두가지 목적을 지닌다.

var func = function (a,b,c,d) {
	console.log(this, a,b,c,d);
}

var bindFun1 = func.bind({x: 1}); // this를 미리 적용하는 것
bindFun1(5,6,7,8);

var bindFun2 = func.bind({x: 1}, 4,5); // this를 미리 적용하는 것 + 부분적용함수 구현
bindFun2(7,8); // {x:1}, 4,5,7,8
bindFun2(10,11); // {x:1}, 4,5,10,11

4) 화살표 함수

ES6에 추가된 화살표 함수는 내부에 this가 아예 없으며, 접근하고자 하면 스코프 체인상 가장 가까운 this에 접근하게 된다.
변수 this를 우회하거나 call/apply/bind 적용할 필요 없어 간결하고 편리하다.

var obj = {

	inner: () => {
    	console.log(this); // obj
    }
    
    inner();
}

📚 정리

📌암묵적 this 바인딩

📝 전역공간에서 this는 전역객체를 참조한다.
📝 어떤 함수를 메서드로서 호출한 경우 this는 메서드 호출 주체를 참조한다.
📝 어떤 함수를 함수로서 호출한 경우, this는 전역객체를 참조한다.
📝 콜백함수내부에서 this는 해당 콜백함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며 정의하지 않은 경우 전역객체를 참조한다.
📝 생성자 함수에서의 this는 생성될 인스턴스를 참조한다.

📌 명시적 this 바인딩

📝 call, apply메서드는 this를 명시적으로 지정하면서 함수 또는 메서드를 호출한다.
📝 bind메서드는 this 및 함수에 넘길 인수를 일부지정하여 새로운 함수를 만든다.

profile
개발하는 프론트엔드개발자

0개의 댓글