코어 자바스크립트 03this

dabin *.◟(ˊᗨˋ)◞.*·2021년 8월 5일
0

Javascript

목록 보기
21/25
post-thumbnail

처음 보는 개념이라 이해하기 어렵지만,,, 요약하다보면 언젠간 되리라 믿는다,,, 화이팅@_@!!

자바스크립트에서 this는 어디서든 사용할 수 있다. this는 함수와 객체(메서드)의 구분이 느슨한 자바스크립트에서 이 둘을 구분해준다.

✔ 상황에 따라 달라지는 this

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

전역공간에서

런타임 환경에 따라 다른 이름과 정보를 가진다. 브라우저-window, Node.js-global.

console.log(this);
console.log(window);
console.log(this===window); //true

//전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다. 
var a =1;
console.log(a); //1(window. 생략된 것)
console.log(window.a); //1
console.log(this.a); //1
//주의
/*
전역변수로 선언한 경우 삭제가 되지 않음
자동으로 전역객체의 프로퍼티로 할당하며 해당 프로퍼티의 변경 및 삭제가능성을  false로 정의
*/
var a = 1;
delete window a; //false
//처음부터 전역객체의 프로퍼티로 할당한 경우 삭제 가능
window b = 2;
delete window b; //true
/*
var로 선언한 전역변수와 전역객체의 프로퍼티는 
호이스팅 여부, 변경 및 삭제 가능성 여부에서 차이를 보임
*/

메서드로 호출할 때

메서드는 독립적인 기능을 수행하는 함수와 달리 자신을 호출한 대상 객체에 관한 동작을 수행한다. 메서드 호출 주제(메서드명 앞의 객체) 참조한다.

//메서드로 호출
var func = function (x) {
  console.log(this, x);
}  
func(1); //Window {...} 함수로 호출 

var obj = {
  method :func
};
obj.method(2); //{method: f} 2 메서드로 호출

함수로 호출할 때

함수 내부에서 this는 전역객체를 참조한다. 어떤 함수를 함수로 호출하면 this가 지정되지 않기 때문이다. 함수로서 호출하는 것은 호출 주제를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이다.(설계상의 오류라고 지적당하기도 한다.)
메서드 내부함수에서 this 또한 전역객체를 참조한다.

var obj1 = {
  outer: function() {
    console.log(this);//obj1
    //obj1.outer() 메서드로서 호출.
    var innerFunc = function () {
      console.log(this);//window
      //함수로서 호출.
    }
    innerFunc();
    
    var obj2 = {
      innerMethod: innerFunc
    };
    obj2.innerMethod();//obj2
    //메서드로서 호출
  }
};
obj1.outer();

위의 예시를 보면 함수 내부인지 메서드 내부인지는 중요하지 않고, 해당 함수를 호출하면 구문 앞에 점 또는 대괄호 표기(메서드 호출)가 있는지 없는지가 더 중요하다는 것을 알 수 있다.

그렇다면, 함수 내부에서 호출 주제가 없을 때 자동으로 전역객체를 바인딩하지 않고, 호출 당시 주변 관경의 this를 그대로 상속받을 수 있는 방법은 없을까? 이는 ES6환경에서 화살표 함수를 사용하여 해결할 수 있게 되었다. 함수 내부에는 this가 없으며, 접근하고자 하면 스코프체인상 가장 가까운 this에 접근한다.

var obj = {
  outer : function() {
    console.log(this);
    var innerFunc = () => {
      console.log(this);
    };
    innerFunc();
  }
};
obj.outer();

화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠진다. 따라서 상위 스코프의 this를 그대로 활용할 수 있다.

콜백함수 내부에서

해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며, 정의하지 않은 경우에 전역객체를 참조한다.

// 300ms 만큼 시간 지연한 뒤 콜백 함수를 실행하라는 명령
// 0.3초 뒤 전역객체 출력
// this 지정 x, 전역객체
setTimeout(function () {console.log(this); }, 300);

//전역객체와 배열의 각 요소 5회 출력
// this 지정 x, 전역객체
[1, 2, 3, 4, 5].forEach(function (x) {
  console.log(this, x);
});

//이벤트 발생할 때 마다 이벤트 정보를 콜백 함수의 첫 번째 인자로 삼아 함수 실행
//버튼 클릭시 앞서 지정한 엘리먼트와 클릭 이벤트에 관한 정보가 담긴 객체 출력
//.addEventListener은 자신의 this를 상속하도록 정의되어 있음. .의 앞부분이 this
document.body.innerHTML += '<button id="a">클릭</button>;
document.body.querySelector('#a')
	.addEventListener('click', function (e) {
  		console.log(this,e);	
	});

생성자 함수에서

생성될 인스턴스를 참조한다.

생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수다. 객체지향 언어에서 생성자를 클래스, 클래스를 통해 만든 객체를 인스턴스라고 한다. 생성자는 구체적인 인스턴스를 만들기 위한 일종의 틀이라고 할 수 있다. 이 틀에는 해당 클래스의 공통 속성이 준비되어 있고, 인스턴스의 개성을 더해 개별 인스턴스를 만들 수 있다.

자바스크립트는 함수에 생성자로서의 역할을 부여했다. new 명령어와 함수를 호출하면 해당 함수가 생성자로 동작한다. 어떤 함수가 생성자 함수로 호출된 경우, 내부의 this는 곧 새로 만들 인스턴스가 된다.

var Dog = function (name, age) {
  this.bark = '멍멍';
  this.name = name;
  this.age = age;
};
var darling = new Dog('달링', 6);
console.log(darling)
/*
{
  age: 6,
  bark: "멍멍",
  name: "달링"
}
*/

✔ 명시적 this 바인딩

call & apply

명시적으로 지정하며 함수나 메서드 호출

call

메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령이다. call 메서드의 첫 번째 인자를 this로 바인딩하고, 이후 인자들을 호출할 함수의 매개변수로 한다.

Function.prototype.call(thisArg[,arg1[,arg2[, ...]]])
//함수
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

//메서드
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

apply

call 과 기능은 동일하다. 하지만 apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수이 매개변수로 지정한다.

Function.prototype.appy(thisArg[, argsArray])
//함수
var funcc = 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

call/apply 메서드 활용하기

생성자 내부에서 다른 생성자 호출

생성자 내부에 다른 생성자와 공통된 내용이 있을 경우 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;
}

var db = newStudent('달링', 'male', '귀염대');

여러 인수 묶어 하나의 배열로 전달하고 싶을 때

여러 개의 인수를 받는 메서드에게 하나의 배열로 인수를 전달하고 싶을 때 사용한다.

//Math.max와 Math.min 메서드를 적용해 배열의 최대/최솟값을 구해보자.
var numbers = [10, 30, 55, 43, 52];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max, min); // 55 10

call/apply 메서드는 this를 예측하기 어렵게 만들어 해석을 방해한다는 단점이 있다. 하지만 ES5 이하 환경에서 대안이 없어 광범위하게 활용되고 있다.

bind

함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 두 가지 목적을 지닌다. call과 비슷하지만, 즉시 호출은 하지 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 한다.

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

var bindFunc1 = func.bind({x: 1});
bindFunc1(5, 6, 7, 8); // {x:1} 5 6 7 8

//bind 메서드를 적용하면 name 프로퍼티에 bound 라는 접두어가 붙는다. 
console.log(bindFunc1.name); //bound func

별도의 인자로 this를 받는 경우(콜백함수 내에서)

콜백 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체(thisArg)를 인자로 지정할 수 있는 경우가 있다. 주로 배열 메서드에 많이 포진되어 있다.

var report= 
    sum:0,
    count:0,
    
    add: function(){
    //arguments를 배열로 변환해 args 변수에 담고
      var args = Array.prototype.slice.call(arguments);
      //이 배열을 순회하며 콜백함수 실행
      args.forEach(function (entry) {
        this.sum += entry;
        ++this.count;
        //forEach 함수의 두번째 인자로 전달한 아래 this가 바인딩 된다.
      }, this);
    },
    average: function () {
      return this.sum / this.count;
    }
};
report.add(60, 85, 95);
//세 인자를 배열로 만들어 forEach 메서드가 실행된다. 
//콜백함수 내부에서 this는 add 메서드에서의 this(report)를 가리킴
//따라서 배열 세 요소를 순환하며 값이 차례로 바뀜
console.log(report.sum, report.count, report.average()); 
//240 3 80
profile
모르는것투성이

0개의 댓글