from : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
http://blog.pluralsight.com/understanding-javascript-prototypes
자바스크립트의 객체 및 prototype 속성에 대한 몇가지 테스트
개요
Prototype 기반 프로그래밍은 클래스가 없는 언어에서 객체지향 프로그래밍을 하기 위한 수단이며 부모 객체를 재사용하여 자식 객체를 생성하는 방법이다.
Prototype으로 존재하는 객체에 속성과 함수를 더 추가하는 방식으로 일반 객체지향 언어의 상속성을 구현할 수 있는 수단을 제공한다.
그러므로 이와같은 방법은 class-less, prototype-oriented, or instance-based programming 이라는 용어로 알려져 있다.
Prototype 기반 프로그래밍의 기원은 Self 라는 언어인데 David Ungar 와 Randall Smith가 설계했다고 알려졌고 요즘 그 인기를 더해가고 있는 추세이다.
Prototype 기반 프로그래밍은 Javascript 를 비롯하여 Cecil, NewtonScript, Io, MOO, REBOL, Kevo, Squeak 과 같은 많은 언에서 채택하고 있다.
자바스크립트의 모든 객체들은 Object 객체를 상속하고 있으므로 Object 객체의 속성과 메소드는 모든 객체에 포함되어 있다.
자바스크립트에서는 클래스를 선언하지 않고 생성자 함수를 통해 객체를 생성하므로 클래스를 사용하는 언어에 익숙한 개발자라면 자바스크립트의 객체 생성 방식이 다소 혼란스러울 수도 있다.
생성자 함수 선언
function Person() { }
생성자 함수를 이용한 객체생성
var person1 = new Person();
var person2 = new Person();
객체의 속성 선언 및 속성의 사용
function Person(gender) {
this.gender = gender;
}
var person1 = new Person('남자');
var person2 = new Person('여자');
alert(person1.gener); //남자
객체의 메소드 선언 및 사용
function Person(_name) {
this.name = _name;
this.getName = function(){ return this.name;};
}
JSON (Javascript Object Notation ), JSON 방법을 이용한 객체의 생성 및 사용
var obj = {
name:'홍길동',
num:5,
toString:function() {
return '번호:'+this.num+', 이름:'+this.name;
}
}
alert(obj); // obj.toString()함수가 호출된다
alert(obj instanceof Object); // true
자바스크립트의 모든 함수(생성자함수 포함)는 선언 만으로도 자바스크립트 객체(Function Object)가 된다
자바스크립트의 함수를 선언하면 자바스크립트의 Function생성자함수가 실행되어 Function객체가 생성되고 선언된 함수의 정보로 초기화된다.
모든 함수는 웹브라우저가 화면에 로드될 때 메모리에 생성되어 실행될 준비가 된다. 실행될 준비가 된 함수는 함수객체(Function Object)로 존재하므로 함수를 선언만 해도 함수 이름으로 함수객체를 접근할 수 있는 것이다. 자바스크립트의 모든 함수객체(Function Object)는 prototype 이라는 속성을 가지므로 생성자 함수객체도 prototype 속성을 갖는 것은 당연하다. 그리고 모든 자바스크립트 객체들이 가지는 __proto__ 라는 속성도 아울러 가진다
자바스크립트의 모든 객체가 가지고 있는 __proto__ 속성은 객체가 참조해야 할 prototype 객체를 링크로 연결하는 기능을 한다.
prototype 속성의 핵심가치
동일한 생성자함수를 통해 생성된 객체들은 prototype에 선언된 속성과 함수들을 공유할 수 있으므로 동일 생성자로부터 많은 객체를 생성하여 사용할 때는 메모리를 상당히 절감할 수 있는 장점이 있다
prototype 속성 중에는 constructor 속성이 있는데 이곳에 해당 함수객체의 생성자 함수 소스코드가 포함되어 있으므로 하위 객체에서 상속할 때 참조해야 할 이유가 된다.
객체가 생성된 이후라도 해당 함수객체의 prototype에 동적으로 속성이나 함수를 추가하면 모든 하위 객체에서 상속하게된다
예를 들어 자바스크립트 상속체계의 최상위 객체를 생성하는 Object 생성자의 prototype에 test() 라는 함수를 추가하면 자바스크립트의 모든 객체는 test()를 상속하여 포함하게된다
함수객체의 확인
자바스크립트의 함수객체는 Function() 생성자를 통해서 생성된 Function 인스턴스이며 이로인하여 length, arguments 등의 속성과, call, apply 등의 메소드가 포함되어 있고 __proto__속성과 prototype 속성을 가지고 있고 __proto__ 속성은 상위 함수객체인 Function의 prototype 속성을 가리키고 있기 때문에 함수객체에서 공유되는 속성 혹은 함수를 참조할 수 있다. 이 말은, 함수객체가 Function 객체를 상속해서 생성되었다는 말은 아니므로 주의해야 한다.
자바스크립트의 함수가 선언만으로도 Function Object 가 되는지 확인하는 코드
// 자바스크립트 함수는 선언만 해도 브라우저에 로드되어 함수객체(Function Object)가 된다
function test() {
alert('This is test()');
}
test.call(); // 함수의 이름은 생성된 함수객체의 참조를 가지므로 이렇게 해도 함수를 실행할 수 있다
test();
alert(test.toString()); // 함수의 이름은 함수객체의 참조를 가지므로 객체가 포함하는 멤버함수를 호출할 수 있다.
alert( test instanceof Function ); // true
alert( typeof test ); // function
결국 선언된 함수의 이름은 Function Object의 참조를 가지고 있으므로 선언된 함수의 이름(함수객체)으로 현재 함수객체의 prototype속성에 접근할 수 있다는 말이 된다. 다음과 같은 예를 통해 함수객체의 존재를 확인할 수 있다. 함수객체의 prototype 속성은 또 하나의 객체이며 아무런 값도 설정하지 않은 prototype 은 기본적으로 constructor 라는 속성을 가지며 현재 생성자 함수의 모든 소스코드를 가지고 있다
// 함수 선언, 선언된 함수는 웹브라우저에 로드될 때 함수객체(Function Object) 상태가 된다.
function Person(_name) {
this.name = _name;
this.getName = function(){ return this.name;};
}
// 함수이름(Person)으로 함수객체(Function Object)에 접근할 수 있다
alert(Person.prototype.constructor); //위의 Person 생성자 함수의 소스코드가 그대로 출력된다
alert(Person instanceof Function); // true
alert( typeof Person ); // function
alert( Person.__proto__.constructor ); // function Function() { [ Native Code ] }
alert( Person.length ); // 1 (아규먼트 수)
alert( typeof Person.prototype ); // object
alert( typeof Person.__proto__ ); // function
alert(Person.__proto__ == Function.prototype); // true;
// Person 인스턴스 생성
var person = new Person('홍길동');
alert( person.prototype ); // undefined
alert( person.__proto__.constructor ); // Person 생성자 함수의 소스코드가 출력됨
alert( typeof person.__proto__ ); // object
alert( person.__proto__ == Person.prototype ); // true
alert( person instanceof Function); // false;
alert(person instanceof Person); // true;
생성자함수가 실행되어 인스턴스(객체)가 생성될 때 해당 인스턴스의 속성으로 __proto__ 라는 오브젝트도 생성된다.
생성자 함수도 일반 함수와 별로 다르지 않으므로 함수객체로 존재할 때 prototype 속성을 갖게 될 것이다.
생성자 함수가 호출되어 실행되면 생성자 함수에 정의된 인스턴스가 생성되고 그 인스턴스는 생성자 함수의 this 키워드에 할당된다.
생성된 일반객체는 함수객체와 달리 prototype 속성을 가지지 않으며 대신 비슷한 __proto__ 라는 속성을 갖게 된다.
함수객체(Function Object)도 일반객체와 마찬가지로 __proto__ 속성을 가지고 있으며 일반객체에는 없는 prototype 속성도 추가적으로 가진다
자바스크립트의 모든 객체가 가지는 __proto__ 속성은 해당 객체가 참조해야할 prototype 속성을 링크로 가지고 있다
함수객체의 __proto__ 속성은 현재의 함수객체가 참조할 prototype 객체의 링크를 가지고 있으며 위에서 선언한 Person 생성자 함수객체의 __proto__속성은 Function.prototype 을 참조하고 있는 것으로 확인된다.
Person 함수객체의 __proto__ 속성이 Function.prototype 속성을 링크로 가리키는 이유는 Function 객체의 prototype 속성에는 생성될 객체나 하위 객체들이 공유해야 할 속성이나 함수가 선언되어 있기 때문일 것이다.
Person 생성자함수를 통해서 인스턴스를 생성하면 해당 인스턴스 안에 포함된 __proto__ 속성이 가리키는 prototype은 이제 더 이상 Function.prototype가 아니라 인스턴스를 생성한 생성자 함수객체의 prototype를 가리킨다. 그러므로 인스턴가 생성되면 person.__proto__ == Person.protype 라는 식이 성립한다. 그러므로 이를 통해 모든 Person 인스턴스들은 Person.prototype 에 선언된 속성이나 함수를 공유할 수 있을 것이다.
__proto__속성과 prototype 속성은 함께 유기적으로 사용되어 Access Chain 을 형성한다
__proto__속성과 prototype 속성은 서로 함께 사용되어 Access Chain을 형성하며 하위객체에서 찾을 수 없는 속성이나 함수가 있다면 Access Chain 을 이용하여 상위 객체의 멤버에서 찾을 수 있도록 하고 있다.
자바스크립트에서 선언한 함수는 자바스크립트의 내장객체인 Function의 인스턴스이기 때문에 함수객체의 안에 length, arguments 등의 속성과, call, apply 등의 메소드가 포함되어 있고 또한 prototype 이라는 객체와 __proto__라는 객체도 속성으로 포함되어 있다. 이는 자바스크립트의 어딘가에 아래와 같은 코드가 포함되어 있다고 볼 수 있다.
Function.prototype = {
arguments: null,
length: 0,
call: function(){
// secret code
},
apply: function(){
// secret code
}
...
}
상속을 구현할 때 prototype 속성 설정하기 및 prototype 속성에 대한 몇가지 테스트
위에서 선언한 Person 객체를 상속하여 Student 객체를 생성하기 위하여 아래처럼 작성해야 한다
function Student(_name, _school) {
this.base = Person;
this.base(_name);
this.school = _school;
this.toString = function(){
alert('이름='+this.name+', 학교='+this.school);
}
}
Student.prototype = Person.prototype;
Student.prototype.constructor = Student;
// 상속 설정 끝
//실행 테스트
Student.prototype.nationality = 'Korea'; // Student 객체들이 공유할 속성 선언
var student = new Student('홍길동', '한국대학교');
var student2 = new Student('장길산', '강북대학교');
alert(student.nationality); // Korea
alert(student2.nationality); // Korea
Student.prototype.nationality = 'USA'; // 공유하는 속성의 값을 변경하면 모든 Student 객체들이 참조하는 값이 변경된다
alert(student.nationality); // USA
alert(student2.nationality); // USA
Student.prototype = {nationality:'Canada'};//prototype 이 참조하는 객체자체를 변경하더라도 기존 객체의 참조값이 변경되지 않는다
alert(student.nationality); // USA
alert(student2.nationality); // USA
var student3 = new Student('스미스', '토론토대학교'); //새로 생성되는 객체는 변경된 prototype의 참조한다
alert(student3.nationality); // Canada
var student4 = new Student('김인철', '강남대학교');
var student5 = new Student('송준철', '동서대학교');
Student.prototype.nationality = 'Korea';
alert(student4.nationality); // Korea
alert(student5.nationality); // Korea
student4.nationality = 'USA'; // student4 객체에 없던 속성이 추가된다
alert(student4.nationality); // USA
alert(student5.nationality); // Korea, student5 객체에는 여전히 없는 속성이므로 __proto__ 속성이 가리키는 prototype 속성에서 찾는다
student4.__proto__.nationality = 'USA'; // __proto__ 속성이 가리키는 prototype 속성에서 값을 변경하므로 모든 객체에서도 변경된다.
alert(student4.nationality); // USA
alert(student5.nationality); // USA
Object 객체의 prototype에 동적으로 함수를 추가하여 모든 하위객체들에게 해당 함수를 추가하는 예
Object.prototype.myMethod = function(){
alert('이 메소드는 Object 객체에 동적으로 추가되어 하위객체들에게도 사용됩니다')
}
var student6 = new Student('홍길동','강북대학교');
student6.myMethod();