[JS] this

CheolHyeon Park·2022년 11월 19일
0

JavaScript

목록 보기
14/23

Java의 기능을 가져온 Javascript에서 this의 동작 방식은 Java와 많이 다르다. 클래스 기반의 Java의 this는 자기 자신, 인스턴스를 가르킨다. 하지만 Javascript의 this는 상황에 따라 달라진다.

  • Java
class Student {
	private String name;
    
    public Student(String name) {
    	this.name = name;
    }
    
    public sayName() {
    	System.out.print("my Name is" + this.name);
    }
}

class Main {
	public static void main(String[] args) {
    	Student cheol = new Student("철현");
        cheol.sayName();
    }
}
  • Javascript의 생성자 함수
var func = function() {
  console.log(this); // window (전역객체(browser))
}
func();

Javascript를 이해하는데 this의 이해가 꼭 필요하기 때문에 정리하고자 한다.

this의 동작 방식

명시하지 않은 this 바인딩

전역 컨텍스트에서 this는 전역 객체를 가리킨다. 그 외 this바인딩은 실행 컨텍스트가 생성되는 시점에 발생한다. 실행 컨텍스트의 실행 시점은 함수 실행 시점임으로, 함수를 정의할 때가 아니라 함수를 호출할 때, 바인딩 된다. 또한, this바인딩 이해의 중요 포인트는 함수로 호출 되었는지, 메서드로 호출 되었는지를 구분해야 한다는 점이다.

💡 함수와 메서드
Javascript에서 함수와 메서드의 차이는 독립성으로 구별할 수 있다. 함수는 주체(객체)에 의해서 호출되는 것이 아니라 독립적으로 호출되는 것을 의미하고, 메서드는 특정 주체에 의해서 호출이 되는 것을 의미한다.

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

좀 더 직관적인 구분은 .[]로 구분하는 것이다. .의 앞부분이 호출 주체고 호출 주체가 this에 바인딩 되게 된다.

콜백, 메서드, 함수에서 this

함수로서 호출하게 되면 this는 전역 객체에 바인딩 하고 메서드로 호출하면 호출 주체에 바인딩 된다. 이는 callback 함수, 메서드 내부에 선언된 함수에도 해당 된다. 단, 제어권을 넘기는 callback의 경우(addEventListener)는 this가 해당 객체로 바인딩 되어 있다.

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

[1,2,3,4,5].forEach(function(x) {console.log(this, x)});  // window

document.body.innerHTML += `<button id="a">클릭</button>`;
document.body.querySelector('#a').addEventListener('click', function(e) {
  console.log(e, this); // this가 id a인 element
})

생성자 this

new 키워드로 생성한 함수의 경우 생성자로서 작동한다. 이 때 내부의 this는 인스턴스를 가리키게 된다.

var Cat = function(name, age) {
  this.name = name;
  this.age = age;
  this.bark = '야옹';
}

var choco = new Cat('초코', 7)
var navi = new Cat('나비', 5)
console.log(choco, navi);  // Cat { name: "초코"....

명시적 this 바인딩

this바인딩을 명시적으로 하는 방법도 있다. call,apply,bind를 사용한다면 해당 함수의 this를 직접 명시할 수 있다.

call

// 일반함수
var func = function(a, b, c) {
  console.log(this, a, b, c)
}

func.call({x: 1}, 1, 2, 3)

// method 
var obj = {
  a: 1,
  method: function(b, c) {
    console.log(this.a, b, c)
  }
};

obj.method(2,3);
obj.method.call({a: 4}, 5,6);

apply

var numbers = [10,20,30,40,50];
// ES6 이전
console.log(Math.max.apply(null, numbers))  // 50
console.log(Math.min.apply(null, numbers))  // 10

// ES6+

console.log(Math.max(...numbers))
console.log(Math.min(...numbers))

bind

바인드는 함수의 생성과 적용을 분리하여 사용할 수 있어 유용하다. 또한, function의 name 프로퍼티의 이름에 "bound"가 붙어서, 어떤 함수를 바인드 했는지 확인할 수 있다. 이를 통해 디버깅에도 유용하다.

  • name 프로퍼티 확인
var func = function(a, b, c) {
  console.log(this, a,b,c);
}

var x1bindFunc = func.bind({x:1});

console.log(x1bindFunc.name)  // bound func
console.log(func.name) // func
x1bindFunc(3,5); // {x:1} 4 5 6
  • 내부 함수에 this 전달
var obj = {
  logThis: function() {
    console.log(this)
  },
  logThisStart1: function() {
    setTimeout(this.logThis, 500);
  },
  logThisStart2: function() {
    setTimeout(this.logThis.bind(this), 500)
  }
}

obj.logThisStart1(); // window
obj.logThisStart2(); // obj

명시적 this 바인딩을 통해 유사배열객체에 배열메소드 사용하기

유사배열객체(length 프로퍼티를 갖고, 0 또는 양수의 프로퍼티를 갖는 객체)는 배열이 아니기 때문에 Array메소드를 사용할 수 없다. 하지만 위 메소드를 이용하여 push, slice와 같은 배열 메소드를 사용할 수 있다.

function a() {
  var argv = Array.prototype.slice.call(arguments);

  argv.forEach(function (arg) {
    console.log(arg);
  })
}
a(1,2,3,4,5,6);

document.body.innerHTML = `<div>a</div><div>b</div><div>c</div>`;
var nodeList = document.querySelectorAll('div');
var nodeArr = Array.prototype.slice.call(nodeList);
nodeArr.forEach(function(node) {
  console.log(node);
})

a함수의 argumentsnodeList는 대표적인 유사배열객체이다. 이를 slice메소드를 call로 호출하여 배열로 복사했다. 이를 통해 nodeArrargvforEach문을 사용할 수 있게 됬다.

profile
나무아래에 앉아, 코딩하는 개발자가 되고 싶은 박철현 블로그입니다.

0개의 댓글