Core Javascript #3 this

Jinmin Kim·2020년 10월 22일
0

this

: 다른 대부분의 객체지향 언어에서는 this는 클래스로 생성한 인스턴스 객체이나,
javascript의 this는 어디서든 사용이 가능하다.
함수와 객체의 구분이 느슨한 자바스크립트에서 this는 실질적으로 이둘을 구분하는 거의 유일한 기능이다. this는 기본적으로 실행 컨택스트가 생성될때 함께 결정되게된다. 즉 함수를 호출할때 결정된다고 할수있다.

상황에 따른 this

전역공간의 this

console.log(this) // window(브라우저), Node.js(global)
console.log(this === window) //true

전역 공간에서 this는 전역객체를 가리킨다.

var a = 1;
window.a = 1;
delete a;
console.log(a);
//왜 값이 다른가 
//chrome -> x
//vscode -> 1
var a = 1;
console.log(a); //1
console.log(window.a); //1
console.log(this.a); //1

전역공간에서의 this는 전역객체를 의미하므로 a를 호출했을경우에
1이 출력되는것이 맞다. 위의 결과 즉 현재의 모든 a의 값이 1인 이유는
자바스크립트의 모든 변수는 실은 특정 객체의 프로퍼티로 동작하기때문이다. 특정 객체라는것은 실행 컨택스트의 LexicalEnvironment를 의미한다.
어떻게보면 이러한상황에서 전역변수 선언과 전역객체의 프로퍼티 할당이 같다라고 볼수도있겠지만, 다른점이 생긴다. delete 명령이다.

var a = 1;
delete a; // false

window.a = 1;
delete window.a; //true

전역객체의 프로퍼티라고 생각한다면 delete 명령어를 사용하여서 동작하는것이 이해가간다. 이것은 전역변수를 선언하면 자바스크립티 엔진이 자동으로 전역객체의 프로퍼티로 할당하면서 프로퍼티의 configurable 속성(변경 및 삭제 가능성)을 false로 정의하여서 삭제가 되지않는다.

메서드 내부에서의 this

함수를 실행할때는
1.함수로써 호출하는방법
2.메서드로써 호출하는방법

두가지가있는데 이둘을 구분하는 방법은 독립성에 있다.
함수는 자체의 독립적인 기능인반면에 메서드는 객체에 관한 동작을 수행한다.
함수로써 호출하는것도 메서드로써의 호출을 구분하는 방법은
함수앞에(.)이있는지 없는지가 구분하는 방법이된다.
(대괄호 표기법도 마찬가지)

var obj = {
	method : function() { console.log(this);},
    inner : {
    	methodB: function() { console.log(this);}
        }
};
obj.method(); // { method: f, inner:{ }} === obj
obj['method'](); //{ method: f, inner:{ }} === obj

obj.inner.methdB(); //{methodB: f} === obj.inner
obj.inner['methdB'](); //{methodB: f} === obj.inner

함수 내부에서 this

함수에서의 this는 호출한 주제가 명시되어있지않기때문에,
this는 전역객체를 바라보게된다.

var obj1 = {
    outer : function(){
        console.log(this); //1
        var innerFunc = function(){
            console.log(this);
        }
        innerFunc(); //2

        var obj2 = {
            innerMethod : innerFunc
        };
        obj2.innerMethod(); // 3
    }
};
obj1.outer();

1,2,3 각각의 호출되는 값은 obj1, window(global), obj2이다
1번은 obj1을 출력하는것은 알겟고, 2번은 window가 출력이 되냐면
메소드로서 실행된것이 아닌 함수로 실행됫기때문에 참조되는 지역객체가 없다. 즉 전역객체로 출력하게되고, 3번은 obj2의 메소드로 출력했기때문에 obj2가 나타나게된다.

self 이용

var obj = {
     outer : function (){
         console.log(this); //1
         var innerFunc1 = function (){
             console.log(this); //2
         };
         innerFunc1();

         var self = this;
         var innerFunc2 = function (){
             console.log(self); //3
         };
         innerFunc2();
     }
 };
 obj.outer();

내부함수에서 this를 우회하는 방법이 있는데 위와같이
this를 다른변수에 저장해두고 그 변수를 이용하는것이다.
위의 self가 this를 대체하는 변수명으로 많이 사용되는 변수이다.

=>(화살표 함수)

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

=>를 사용하게되면 this 바인딩 과정이 빠지게되어 상위 this를 그대로 사용할수있다.

생성자 함수 내부 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); 

생성자 함수를 호출하게되면 객체를 만들게되고 그 객체에 공통속성을 부여한다. 즉 각각의 인스턴스에 접근을 하게되는것.

명시적 this 바인딩

call 메서드

Function.prototype.call(thisarg, arg1, arg2...)

call 메서드는 호출 주체인 함수를 즉시 실행하도록 하는 함수이다.
함수를 그냥 실행하면 this는 전역객체를 참조하지만 call 메서드를 이용하면 임의의 객체를 this로 지정할수있다.

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

Function.prototype.apply(thisarg,[ argsArray])

apply 메서드는 call 메서드와 기능적으로 완전히 동일하나
두번째 인자를 배열로 받아서 그배열의 요소들을 함수의 매개변수로 사용한다는것이 차이점이다.

var func = function (a, b, c){
    console.log(this, a, b, c);
};
func.apply( func, [4, 5, 6]); // func 4 5 6
func(1, 2, 3); // window 1 2 3
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 메소드를 사용하여서
활용할수있다.

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

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

 var arr = Array.prototype.slice.call(obj);
 console.log(arr); // [ 'a', 'b', 'c', 'd']
function a(){
     var argv = Array.prototype.slice.call(arguments);
     console.log(argv);
     argv.forEach(function(arg){
     console.log(arg);
    });
 }
 a(1, 2, 3); 
ES6부터 사용가능한 유사배열객체 또는 순회 가능한 모든 종류의 데이터 타입을 배열로 전환하는 Array.from()
var obj = {
    0 : 'a',
    1 : 'b',
    2 : 'c',
    length : 3
};
var arr = Array.from(obj);
console.log(arr); // ['a', 'b', 'c']
생성자 내부에서 다른 생성자를 호출하여서 간단하게 반복을 줄이는 방법
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.apply(this, [name, gender]);
    this.company = company;
}

var by = new Student('보영', 'female', '단국대');
var jn = new Employee('재난', 'male', '구골');

console.log(by);
console.log(jn);
여러 인수를 묶어 하나의 배열로 전달 (apply)
// 원래의 최대/ 최소구하기
var numbers = [10, 20, 3, 16, 45];
var max = min = numbers[0];
numbers.forEach(function(number){
    if(number > max){
        max = number;
    }
    if(number < min){
        min = number;
    }
});

console.log(max, min);
// 여러 인수를 받는 메서드
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);
// ES6의 펼치기 연산자 활용
const numbers = [10, 20, 3, 16, 45];
const max = Math.max(...numbers);
const min = Math.min(...numbers);
console.log(max, min);

bind 메서드

Function.prototype.bind(thisArg, arg1, arg2,....);

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

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

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

var bindFunc2 = func.bind({x : 2}, 1, 2);
bindFunc2(3, 4);
bindFunc2(8, 9);

name 프로퍼티

bind 함수를 사용하면 독특한성질을 가지게되는데 name 프로퍼티에
bound라는 접두어가 붙게된다.

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

 var bindFunc = func.bind({ x : 1}, 4, 5);
 console.log(func.name); //func
 console.log(bindFunc.name) // bound func

상위 컨택스트 this를 내부함수나 콜백함수로

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

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

메서드의 내부함수에서 메서드의 this를 보게하기위한 방법으로 self를 사용했었는데 위와같은 방법을 사용하여도 가능하다.

내부함수에 this 전달
 var obj = {
     logThis : function() {
         console.log(this);
     },
     logThisLater1 : function(){
         setTimeout(this.logThis, 500);
     },
     logThisLater2 : function(){
         setTimeout(this.logThis.bind(this), 1000);
     }
 };

 obj.logThisLater1(); // Window
 obj.logThisLater2(); // obj{....}

내부함수로 this를 전달할때 bind를 사용하면 다른방법으로 쉽게 반영이 가능하다.

끝👌👏

!!이내용은 코어 자바스크립트(정재남지음) 책을보고 정리한 내용입니다

profile
Let's do it developer

0개의 댓글