본문 바로가기

NodeJS/javascript

자바스크립트 prototype

자바스크립트 상속

자바스크립트의 상속은 타 언어랑은 조금 다르다. 상속을 지원하는 언어들하고는 다르게 상속 패턴을 하나하나 다 구현을 해 주어야 한다. 먼저 펙토리 메서드를 보자.

펙토리 메서드

function createPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
        alert(this.name);
    };
    return 0;
}

위의 함수는 Person 객체를 만드는 데 필요한 정보를 매개변수로 받아서 객체를 생성한다. 계속 호출해도 항상 프로퍼티 세 개랑 메서드 한 개를 가진 객체를 반환하는 것이다. 그러면 문제가 해결되는 거냐? NO

가장 큰 문제는 비슷한 객체를 여러 개 만들 때 코드 중복은 해결되나 생성한 객체가 어떤 타입인지 알 방법이 없다는 것이다.

생성자 패턴

function Person (name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        alert(this.name);
    };
}

var person1 = new Person("jongun", 25, "IngYuo");
var person2 = new Person("ddd", 20, "programmer");

위의 패턴이 생성자 패턴이다. 즉, 세 가지 파라미터를 인자로 받아서 그걸 가지고 new 명령어를 사용, 객체화시키는 것이다. 특이한 점은

명시적으로 객체를 생성하지 않고 
프로퍼티와 메서드는 this 객체에 할당되며 
return 문이 없다는 것이다.

new 연산자를 사용하면 내부적으로 다음과 같은 과정이 이루어진다. 
1. 객체를 생성한다. 
2. 생성자의 this 값에 새 객체를 할당한다. 
3. 생성자 내부 코드를 실행한다(객체에 프로퍼티를 추가) 
4. 새 객체를 반환한다.

그러나 이렇게 생성을 하더라도 문제가 발생한다. 문제는 sayName이 여러 개 있다는 것이다. 즉 인스턴스마다 메서드가 만들어지므로, 위에서 person1 의 sayName과 person2의 sayName은 다른 메서드라는 것이다. 함수를 밖으로 빼면 문제가 해결되지만, 그러면 전역스코프에 몇몇 객체만 사용하는 메서드가 올라가므로 비효율적이다. 따라서 다음과 같은 해결책이 등장했다.

프로토타입 패턴

function Person () {

}

Person.prototype.name    = "JongUn";    
Person.prototype.age = 25;
Person.prototype.job = "Gamer";
Person.prototype.sayName = function() {
    alert(this.name);
};

var person1 = new Person();
person1.sayName();

var person2 = new Person();
pseron2.sayName();

alert(person1.sayName == person2.sayName); //true

이렇게 하면 생성자 함수는 비어 있지만, 생성자를 호출하여 만든 객체에도 프로퍼티와 메서드가 존재하게 된다. 프로퍼티와 메서드를 모두 같은 인스턴스에서 공유하게 되므로, sayName 함수 또한 공유하게 된다.

내부구조는 어떤 원리로 동작하게 될까?

프로토타입의 동작

  1. 함수가 생성되면, prototype 프로퍼티 역시 특정 규칙에 따라 생성된다. prototype는 자동으로 constructor 프로퍼티를 갖는데. 이건 소속된 함수를 가리킨다. 즉, Person.prototype.constructor === Person 이 되는 것이다.

  2. 다음에는 생성자에 따라서 각종 프로퍼티와 메서드가 추가된다.

  3. 해당 프로터타입은 단지 constructor 프로퍼티만 가지고, 다른 메서드는 Object에서 상속한다. 생성자를 호출하여 인스턴스를 만들 때마다, 해당 인스턴스 내부에는 생성자의 프로토타입을 가리키는 포인터가 생성된다.

이 포인터는 [[Prototype]]이라 부르고, 사바피 파이어폭스, 크롬은 ㅡprotoㅡ라는 프로퍼티를 통해서 이에 접근할 수 있게 한다.

즉 인스턴스와 연결되는 것은 생성자가 아니라 생성자의 프로토타입인 것이다

  1. 객체에서 프로퍼티를 읽으려고 할 때마다, 해당 프로퍼티 이름으로 검색을 시작한다. 검색은 객체 인스턴스에서 시작하고, 찾으면 반환하고 찾지 못하면 프로터타입으로 올라가서 검색을 계속한다. 찾으면 그 값을 반환한다.

    그러니까 먼저 객체 안에서 검색해보고, 없으면 프인터를 타고 올라가서 프로토타입에서 검색하고 결과를 반환하게 된다.

  2. 객체 인스턴스에서 프로퍼티에 있는 값을 수정할 수는 없다. 프로터타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면, 해당 프로퍼티는 인스턴스에 추가되며 프로토타입에 올라가지 않는다.

    흔히 말하는 오버라이드(override)가 된다.


function Person() {
}

Person.prototype.name = "Jongun";
Person.prototype.age = 25;
Person.prototype.job = "IngYeo";
Person.prototype.sayName = function() {
     alert(this.name);
};

var person1 = new Person();
var person2 = new Person();

person1.name = "Change";
alert(person1.name); // "Change" from 인스턴스
alert(person2.name); // "Jongun" from 프로토타입

이런 식의 코드가 작성될 수 있다. delete 연산자를 사용하여 인스턴스에 입력한 값을 지우면 다시 프로토타입에 있는 값을 불러올 수 있다. 즉,

delete person1.name;
alert(person1.name) // "Jongun" from 프로토타입

이 되는 것이다.

hasOwnProperty 메서드를 이용하여 지금 사용한 프로퍼티가 인스턴스의 것인지, 프로토타입의 것인지를 구분할 수 있다.

hasOwnProperty : 프로퍼티가 인스턴스에 존재하면 true, 아니면 false를 리턴한다.

즉 위의 코드에서 person1.hasOwnProperty(“name”) 는 true이지만 person2.hasOwnProperty(“name”)는 false를 return 하게 된다.

또한 in을 섞어서 사용하면(in은 그 인스턴스에서 입력한 프로퍼티가 접근가능하면 true를 리턴해주는 메소드이다)지금 프로퍼티가 인스턴스에 존재하는지, 프로토타입에 존재하는지 알 수 있다.

프로토타입 대체 문법

객체 리터럴로 프로토타입을 덮어써서 반복을 줄이고 가독성을 높이는 패턴이 존재한다.

function Person() {
}

Person.prototype = {
name : "JongUn",
age : 25,
job : "Programmer",
sayName : function() {
    alert(this.name);
    }
};

Person의 프로퍼티에 객체 리터럴로 생성한 객체를 덮어씌운다. constructor 프로퍼티가 Person을 가리키지 않는 다는 것만 제외하면 최종 결과는 완전히 동일하다.

즉 이 문법은 기본 prototype 객체를 완전히 덮어쓰는데, 결과적으로 constructor 프로퍼티는 함수가 아니라 완전히 새로운 객체의 생성자를 가리키게 된다.

constructor가 가리키는 것이 변경되므로 이를 다시 세팅해주려면 다른 방법을 사용해야 한다.

프로토타입의 동적 성질

프로토타입에서 값을 찾는 건 런타임 검색이므로 프로토타입이 바뀌면 내용이 즉시 인스턴스에 반영된다. 즉 다시 말하면..

var friend = new Prerson();
Person.prototype.sayHi = function() {
    alert("HI");
};

friend.sayHi(); //동작한다.

이게 동작하게 된다. 이게 가능한 이유는 인스턴스를 만들 때 프로토타입 전체를 복사한 게 아니라, 프로토타입으로 가는 포인터만 복사했기 때문이다. 그러므로 나중에 값이 추가되더라도 포인터를 타고 가면 프토토타입에 새로 추가된 프로펴티에 접근할 수 있는 것이다.

그러나 프로토타입을 생성한 후에 위를 객체 리터럴로 덮어씌우면 sayHi를 호출할 수 없게 된다. 왜냐하면 객체 리터럴은 새로운 객체를 생성한 것이지, Person Prototype을 대체하는 것이 아니기 때문이다.

프로토타입의 문제점

  1. 초기화 매개변수를 생성자에 전달할 수 없으므로, 모든 인스턴스가 기본적으로 같은 프로퍼티를 갖게 된다. 즉, 자바에서 생성자에 파라미터를 넘겨서 값을 세팅하는 것과 같은 작업을 할 수 없다.
  2. 프로토타입에 존재하는 프로퍼티는 모두 인스턴스에서 공유된다. 이 점은 함수에서는 이상적이지만 그 위에 자료구조를 올리고 한 인스턴스가 값을 수정하면, 모든 인스턴스가 그 영향을 받게 된다.

생성자 패턴 - 프로토타입 패턴의 조합

즉 생성자 패턴으로 인스턴스를 정의하고, 프로토타입 패턴으로 메서드와 공유 프로퍼티를 정의하는 것이다.

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friend = ["Selly", "Brown"];
}

Person.prototype = {
    constructor : Person,
    sayName : function () {
        alert(this.name);
    }
};

var person1 = new Person("JongUn", 25, "IngYeo");
ver person2 = new Person("Selly father", 30, "programmer");

person1.friend.push("Van");

alert(person1.friends); //"JongUn, Selly father, Van"
alert(person2.friends); //"JongUn, Selly father"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

즉 인스턴스 프로퍼티는 위의 코드처럼 생성자에 정의하고, 공유 프로퍼티는 프로토타입에서 정의하는 것이다. 이렇게 하면 person1의 배열에 값을 추가해도 person2의 배열은 영향을 받지 않는다.

동적 프로토타입 패턴

이건 생성자와 프로토타입 구분이 혼란스러울 수 있으니, 모든 정보를 생성자 내부에 캡슐화하여 혼란을 해결하고 필요한 경우에는 프로토타입을 생성자 내부에서 초기화하여 생성자와 프로토타입을 모두 쓰는 장점을 취하려는 접근법이다.

function Person(name, age, job) {
    //프로퍼티
    this.name = name;
    this.age = age;
    this.job = job;

    //메서드
    if(typeof this.sayName != "function) {
        Person.prototype.sayName = function() {
            alert(this.name);
        };
    }
}

var friend = new Person("JongUn", 25, "IngYeo");
friend.sayName(); //JongUn

프로토타입을 초기화하기 전에 if문을 통해서 프로퍼티나 메서드가 존재하는지 확인한 후에, 프로퍼티를 추가한다. 저 코드는 if절 때문에 처음 한 번만 동작하기 때문에 다음부터는 문제가 없게 된다.

기생 생성자(parasitic constructor)패턴

이 패턴은 일반적인 생성자처럼 보이지만 실은 다른 객체를 생성하고 변환하는 동작을 래퍼 생성자로 감싸는 것이다.

function Person(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
        alert(this.name);
    };
    return o;
}

var friend = new Person("Jongun", 25, "programmer");
friend.sayName();

이 예제에서는 생성자가 새 객체를 생성한 후에 프로퍼티와 메서드를 초기화해서 반환한다. 이렇게 하면 생성자가 값을 반환하지 않을 때는 기본적으로 새 객체 인스턴스를 반환한다. 
이 방법을 통해서 다른 방법으로는 불가능한 객체 생성자를 만들 수 있다. 메서드를 추가한 특별한 배열을 만들고 싶다면, Array 생성자에 접근할 수는 없지만 다른 방법으로 제한을 우회할 수 있다.

function SpecialArray() {
//create Array
var values = new Array();
//add Value
values.push.apply(values, argument);
//set method
values.toPipedString = function() {
    return this.join("|");
};

return values;
}

var color = new SpecialArray("red", "blue", "green");
alert(colors.toPipedString()); //"red|blue|green"

위의 코드에서는 SpecialArray라는 생성자를 만든 후에, 배열을 만들고 이 생성자가 받은 매개변수(argument)를 모두 넘겨서 배열을 초기화한다. 그 다음에 안에 있는 toPipedString을 호출하게 된다.

문제점 : 반환된 객체와 생성자, 생성자와 프로토타입 사이에 아무런 관계도 없게 된다. 즉 instanceof 연산자로 객체의 타입을 알아내는 것이 불가능하다. 다른 패턴으로 문제를 해결할 수 없는 경우에만 이 패턴을 사용해야 한다.

방탄 생성자 패턴

방탄 : 공용 프로퍼티가 없고 && 메서드가 this를 참조하지 않는 객체

사용환경은 this, new를 금지하는 보안환경에 가장 잘 어울린다. 기생 생성자랑 흡사하지만 차이점이 있다.

차이점 : 
1) 생성된 객체의 인스턴스 메서드가 this를 참조하지 않는다. 
2) 생성자를 new로 호출하는 경우가 결코 없다.

function Person(name, age, job) {
    //create object to return
    var o = new Object();
    //option : define variable and function

    //set method
    o.sayName = function() {
        alert(name);
    };
    return o;
}

이 패턴에서 주의할 점은 객체의 name에 접근할 방법이 아예 없다는 것이다. sayName을 사용하여 값을 읽어올 수는 있지만 변수에 접근할 방법이 없다. 사용방법은 다음과 같다.

var friend = Person("Jongun", 25, "Programmer");
friend.sayName();

이 객체에 등록된 메서드를 호출하지 않으면 객체의 데이터에 접근할 방법이 전혀 없다. 즉 데이터는 private하게 선언되는 것이다.

상속

프로토타입 체인

기본 아이디어는 프로토타입을 이용하여 두 참조 타입 사이에서 프로퍼티와 메서드를 상속한다는 것이다.

모든 생성자에는 프로토타입을 가리키는 내부 포인터가 있으며, 생성자 자신을 가리키는 프로토타입 객체가 존재한다.

포인트는 다음과 같다. 프로토타입이 실은 다른 타입의 인스턴스라면? 
프로로타입(A)자체에 다른 프로토타입(B)를 가리키는 포인터가 있을 것이고, B는 다른 생성자를 가리키는 포인터가 있을 것이다.

이렇게 패턴이 반복되어 인스턴스와 프로토타입을 연결하는 체인이 형성될 것이다. 코드는 다음과 같다.

function SuperType() {
    this.property = true;
}

SuperType.prototype.getSuperValue = function() {
    return this.property;
};

function SubType() {
    this.subproperty = false;
}

//superType을 상속
SubType.prototpye = new SuperType();
SubType.prototype.getSubValue = function() {
    return this.subproperty;
};

var instance = new SubType();
alert(instance.getSuperValue()); //true

이 코드는 SuperType과 SubType두 개를 정의하는데 각각 타입에는 프로퍼티와 메서드가 한 개 씩 존재한다. 두 타입의 차이는 SubType이 SuperType의 새 인스턴스를 생성하여 SuperType을 상속받고, 이를 SubType.prototype에 할당한다는 것이다.

이러면 앞에서 보듯이 원래 프로토타입을 새로운 객체로 덮어쓰게 되는데, 그러면 SuperType 인스턴스에 존재했을 프로퍼티와 메서드가 SubType.prototype에도 존재하게 된다.

상속한 후에는 SubType.prototype에 메서드를 추가하게 되는데, SuperType에서 상속한 메서드는 그대로 유지된다.

즉 새 프로토타입 객체로 덮어쓰게 되면, 새 프로토타입(여기서는 SuperType)은 SuperType의 프로토타입을 가리키는 포인터도 갖게 된다. 
즉 instance->SubType.prototype->SuperType.prototype 
이렇게 체인을 타고 올라가면서 원하는 프로퍼티를 탐색할 수 있다.

기본 프로토타입

사실 프로토타입 체인에는 한 단계가 더 있다. 모든 참조 타입은 기본적으로 프로토타입 체인을 통해 Object를 상속한다. 
즉 함수의 내부 프로토타입 포인터는 Object.prototype을 가리킨다. 이런 방식으로 toString()이나 valueOf()같은 기본 메소드를 상속하게 된다.

프로토타입과 인스턴스 사이 관계

1) instanceof : 이 연산자는 인스턴스 생성자가 프로토타입 체인에 존재할 때 true를 반환한다.

alert(instance instanceof Object); //true 
alert(instance instanceof SuperType); //true 
alert(instance instanceof SubType); //true

instance객체는 프로토타입 체인에 따라서 SubType,SuperType 모두의 인스턴스기 때문에 true를 리턴하게 된다.

2) isPrototypeOf() : 체인에 존재하는 인스턴스에서 true를 반환한다.

alert(Object.prototype.isPrototypeOf(instance)); //true 
alert(SuperType.prototype.isPrototypeOf(instance)); //true 
alert(SubType.prototype.isPrototypeOf(instance)); //true

메서드

하위 타입에서 상위 타입의 메서드를 오버라이드하거나 상위 타입에 존재하지 않는 메서드를 정의해야 할 때는, 프로토타입이 할당된 후에 필요한 메서드를 프로토타입에 추가해야 한다.

또한 객체 리터러를 사용하여 프로토타입 메서드를 만들면 체인을 덮어쓰게 되므로 프로토타입 체인과 같이 사용할 수 없다.

프로토타입 체인의 문제점

  1. 프로토타입 프로퍼티에 들어있는 참조값이 모든 인스턴스에서 공유된다. 상속을 구현하게 되면, 프로토타입이 다른 타입의 인스턴스가 되므로, 처음에 인스턴스 프로퍼티였던 것들이 프로토타입 프로퍼티로 바뀐다. 즉,

    function SuperType() { 
    this.colors = [“red”, “blue”, “green”]; 
    }

    function SubType() { 
    }

    //SuperTyp에서 상속 
    SubType.prototype = new SuperType();

    var instance1 = new SubType(); 
    instance1.colors.push(“black”); 
    alert(instance1.colors); //”red,blue,green,black”

    var instance2 = new SubType(); 
    alert(instance2.colors); //”red,blue,green,black”

즉 각각의 인스턴스가 자기 자신의 데이터 구조를 보관할 수 없다는 것이다. 상속을 하게 되면 SubType의 모든 인스턴스에서 colors 프로퍼티를 공유하게 되는 것이다.

  1. 하위 타입 인스턴스를 만들 때, 상위 타입 생성자에 매개변수를 전달할 방법이 없다.

생성자 훔치기(constructor stealing)

하위 타입 생성자 안에서 상위 타입 생성자를 호출한다. 새로 생성한 객체에서 call과 apply를 이용하여 생성자를 실행할 수 있다. 다음과 같다.

function SuperType() {
    this.colors = ["red", "blue", "green"];
}

function SubType() {
    SuperType.call(this);
}

var instance1 = new SubType();
instance1.colors.push("black");

alert(instance1.colors); //"red,blue,green,black"

var instance2 = new SubType();
alert(instance2.colors); ////"red,blue,green"

call() 또는 apply()를 사용해서 SuperType 생성자를 새로 생성한 SubType의 인스턴스 컨텍스트에서 호출하게 된다. 이렇게 하면 객체 초기화 코드 전체를 
SuperType 객체에서 실행하는 효과가 있다.

매개변수 전달

생성자 훔치기 패턴은 하위 타입 생성자 안에서 상위타입에 매개변수를 전달할 수 있다.

function SuperType(name) {
    this.name = name;
}

function SubType() {
    //SuperType에서 상속하고, 매개변수를 전달
    SuperType.call(this, "Jongun");
    //인스턴스 프로퍼티
    this.age = 25;
}

var instance = new SubType();
alert(instance.name); //jongun
alert(instance.age); //25

SubType생성자 안에서 SuperType 생성자를 호출할 때 값을 전달할 수 있는데, SubType 인스턴스의 name property를 세팅하는 결과가 된다.

생성자 훔치기 패턴의 문제

메서드를 생성자 내부에서만 정의해야 하므로 함수 재사용이 불가능하다. 상위 타입의 프로토타입에 정의된 메서드는 하위 타입에서 접근할 수 없다.

조합 상속

프로토타입 체인과 생성자 훔치기 패턴을 조합하여 장점을 취하려는 접근법이다.

아이디어 : 프로토타입 체인을 써서 프로퍼티와 메서드를 상속하고, 생성자 훔치기 패턴으로 인스턴스 프로퍼티를 상속한다. 이렇게 하면 메서드를 정의해서 함수를 재사용할 수 있고, 각 인스턴스가 고유한 프로퍼티를 가질 수도 있다.

function SuperType(name) {
    this.name = name;
    this.color = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function() {
    alert(this.name);
};

function SubType(name, age) {
    //프로퍼티 상속
    SuperType.call(this, name);

    this.age = age;
}

//메서드 상속
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function() {
    alert(this.age);
};

var instance1 = new SubType("jongUn", 25);
instance1.colors.push("black");
alert(instance1.colors);
instance1.sayName();
instance1.sayAge();

var instance2 = new SubType("Selly Father", 30);
alert(instance2.colors);

instance2.sayName();
instance2.sayAge();

SuperType 생성자에서 두 가지 프로퍼티를 정의했고, SuperType 프로토타입은 sayName()이라는 메서드를 가지고 있다. 
즉 만들어놓은 메서드는 프로포타입 체인을 통해서 상속받고, SuperType이 가지고 있는 프로퍼티는 생성자 훔치기 기법을 통해서 상속받는다.

이 패턴은 가장 자주 사용하는 패턴 중 하나이다.

프로토타입 상속

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

object() 함수는 임시 생성자를 만들어서 주어진 객체를 생성자의 프로토타입으로 할당한 다음 임시 인스턴스를 리턴한다. 즉 object()는 주어진 객체의 사본이 된다. 다음과 같은 코드를 보면…

var person = {
    name : "JongUn",
    friends : ["Google", "Yahoo", "JS adv"]
};

var anotherPerson = object(person);
anotherPerson.name = object(person);
antoherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

alert(person.friends); // "Google, Yahoo, JS adv, Rob, Barbie"
  1. 다른 객체의 기반이 되는 객체를 만든다.
  2. 기반 객체를 object에 넘겨서 새 객체를 할당받는다. 이 경우 새 객체의 프로토타입은 person이 된다. 
    그러므로 두 객체(yetAnotherPerson, anotherPerson)은 person.friends를 공유하게 된다.

ECMAScript 5판에서는 프로토타입 상속의 개념을 공식적으로 수용하여 Object.create() 메서드를 추가했다. 이건 매개변수를 두 개 받아서 하나는 다른 객체의 프로토타입이 되는 객체고, 두 번째는 새 객체에 추가할 프로퍼티를 담은 객체이다. 매개변수를 하나만 쓰면 Object.create()는 object()메서드와 동일하게 동작한다.

기생상속

아이디어 : 상속을 담당할 함수를 만들고, 어떤 식으로든 객체를 확장하여 반환한다.

function createAnother(original) {
    var clone = object(original);
    clone.sayHi = function() {
        alert("hi");
    };
    return clone;
}

이 코드에서는 다른 객체의 기반이 되는 객체 하나만 매개변수로 전달받는다. 그 객체를 object에 넘긴 결과를 clone에 할당하고, 거기에 메서를 추가한 후 객체를 반환한다. 즉 original에 있는 프로퍼티와 메서드를 상속받고, 새로운 객체는 sayHi라는 메서드를 추가로 가지는 것이다.

기생 조합 상속

조합 상속에서 가장 비효율적인 면은 상위 타입 생성자가 두 번 호출된다는 것이다. 즉 상속받는 객체 안에서 한 번 호출하게 되고(call을 이용하여), 프로토타입을 덮어쓰는 과정에서 또 한 번 호출하게 된다.

이걸 해결하기 위한 게 기생 조합 상속이다.

아이디어 : 생성자 훔치기를 통해 프로퍼티 상속을 구현하지만 메서드 상속에는 프로토타입 체인을 혼용한다. 즉 기생 상속을 사용하여 상위 타입의 프로토타입을 상속한 다음 결과를 하위 타입의 프로토타입에 할당한다.

function inheritPrototype(subType, superType) {
    var prototype = obejct(superType.prototype); //객체 생성
    prototype.constructor = subType;
    subType.prototype = prototype;
}

우선 이 함수에서는 프로토타입을 넘겨서 하위 객체를 생성한다. 그 후에 그 객체의 constructor 를 할당해주고, 그 객체를 하위 객체의 프로토타입으로 선언한다. 전체 사용 코드는 다음과 같다.

function SuperType(name) {
    this.name = name;
    this.color = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function() {
    alert(this.name);
};

function SubType(name, age) {
    SuperType.call(this, name);

    this.age = age;
}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function() {
    alert(this.age);
};

이 예제는 SuperType 생성자를 한 번만 호출하므로 subType.prototype에 불필요하고 사용하지 않는 프로퍼티를 만들지 않아 효율적이다. 또한 체인이 온전하게 유지된다. 현재 참조 타입에서 가장 효율적인 상속 패러다임으로 평가받고 있다.