this는 함수 내에서 함수 호출 맥락(context)를 의미합니다. 맥락이라는 것은 상황에 따라서 달라진다는 의미인데 즉 함수를 어떻게 호출하느냐에 따라서 this가 가리키는 대상이 달라진다는 뜻입니다. 함수와 객체의 관계가 느슨한 자바스크립트에서 this는 이 둘을 연결시켜주는 실질적인 연결점의 역할을 합니다.
함수를 호출 했을 때 this는 무엇을 가리키는지 살펴보자.function func(){ if(window === this){ document.write("window === this"); } } func();
this는 전역객체인 window와 같습니다.
결과는 'window === this' 가 출력됩니다.
객체의 소속인 메소드의 this는 그 객체를 가르킵니다.
var o = { func : function(){ if(o === this){ document.write("o === this"); } } } o.func();
그 결과는 'o === this'가 출력됩니다.
아래의 코드는 함수를 호출했을 때와 new를 이용해서 생성자를 호출했을 때의 차이를 보여줍니다.
var funcThis = null; function Func(){ funcThis = this; } var o1 = Func(); if(funcThis === window){ document.write('window <br />'); } var o2 = new Func(); if(funcThis === o2){ document.write('o2 <br />'); }
결과는 'window' , 'o2' 가 출력되서 나옵니다.
생성자는 빈 객체를 만듭니다. 그리고 이 객체내에서 this는 만들어진 객체를 가르킵니다. 이것은 매우 중요한 사실이고 , 생성자가 실행되기 전까지는 객체는 변수에도 할당될 수 없기 때문에 this가 아니면 객체에 대한 어떤 작업을 할 수 없기 때문입니다.function Func(){ document.write(o); } var o = new Func();
이 코드의 결과는 'undefined'가 출력됩니다.
함수의 메소드인 apply,call을 이용하면 this의 값을 제어할 수 있습니다.
var o = {} var p = {} function func(){ switch(this){ case o: document.write('o<br />'); break; case p: document.write('p<br />'); break; case window: document.write('window<br />'); break; } } func(); // 결과 : window func.apply(o); // 결과 : o func.apply(p); // 결과 : p
apply를 사용하더라도 실제 func가 속한 객체를 바꾼 것이 아니기 때문에 마지막에 다시 호출한 func()에서는 다시 window 객체를 가리킵니다.
apply와 call 두 메소드는 거의 흡사합니다. 둘의 유일한 차이점은 바인딩 할 함수에 인자가 있을 때 파라미터로 배열을 넣냐, 여러개의 파라미터를 값으로 넣냐입니다. apply는 배열을 인자로 받고, call은 다수의 파라미터를 인자로 받습니다.window.num = 0; function sum(num1, num2){ return console.log(this.num + num1 + num2); } const obj = { num : 100 } sum(1, 2); // 3 sum.call(obj, 1, 2); // 결과 : 103 sum.apply(obj, [1, 2]); // 결과 : 103
예시를 보면 call이나 apply나 인자의 형태만 다를 뿐 바인딩 된 객체나 전달 된 인자들은 모두 같다는 것을 알 수 있습니다.
객체는 연관된 로직들로 이루어진 작은 프로그램이라고 할 수 있습니다. 상속은 객체의 로직을 그대로 물려 받는 또 다른 객체를 만들 수 있는 기능을 의미합니다. 단순히 물려받는 것이라면 의미가 없을 것입니다. 기존의 로직을 수정하고 변경해서 파생된 새로운 객체를 만들 수 있게 해줍니다.
function Person(name){ this.name = name; this.introduce = function(){ return 'My name is '+this.name; } } var p1 = new Person('egoing'); document.write(p1.introduce()+"<br />");
이 코드의 결과는 'My name is egoing' 이 출력됩니다.
위의 코드를 아래와 같이 바꿔보자.function Person(name){ this.name = name; } Person.prototype.name=null; Person.prototype.introduce = function(){ return 'My name is '+this.name; } var p1 = new Person('egoing'); document.write(p1.introduce()+"<br />");
결과는 같습니다. 하지만 상속을 위한 기본적인 준비를 마쳤습니다.
이제 상속의 준비를 마쳤으니 programmer이라는 생성자를 만들었습니다.
function Person(name){ this.name = name; } Person.prototype.name=null; Person.prototype.introduce = function(){ return 'My name is '+this.name; } function Programmer(name){ this.name = name; } Programmer.prototype = new Person(); var p1 = new Programmer('egoing'); document.write(p1.introduce()+"<br />");
그리고 이 생성자의 prototype과 person의 객체를 연결했더니 programmer 객체도 메소드 introduce를 사용할 수 있게 되었습니다.
programmer가 person의 기능을 상속하고 있는 것입니다. 단순히 똑같은 기능을 갖게 되는 것이라면 상속의 의의는 사라질 것입니다. 부모의 기능을 계승 발전할 수 있는 것이 상속의 가치입니다.