Javascript Prototype & Inheritance 윤지수. NEXT
May 21, 2015
objvar obj = { name : “jisu”, getName : function() { return this.name; }}
object 선언객체에 프로퍼티(속성)를 추가
name
getName
objvar obj = { name : “jisu”, getName : function() { return this.name; }}
obj.getName; //jisu
object 선언객체를 사용 (여러번)
name
getName
만약 obj와 같은 객체가 여러개여야하고, 각 객체가 서로 다른 name을 가져야 한다면?
Parentfunction Parent() {}(‘prototype’ in Parent) == true
Prototype
모든 function는 prototype 이라는 객체를 가지고 있다
Parent 의prototype
사실 function뿐 아니라 모든 객체(object) 는 prototype 프로퍼티 객체를 가지고 있음하지만 주로 function을 객체화 한 후 많이 활용함
var obj = {};
obj.constructor.prototype; //확인하기 1{}.__proto__; //확인하기 2
Prototype
사실 function뿐 아니라 모든 객체(object) 는 prototype을 가지고 있음하지만 주로 function을 객체화 한 후 많이 활용함
?> ‘new’ 연산자
Prototype
Parent
function을 ‘new’ 연산자로 호출하면 어떻게 되는가?
function Parent() {}var oP = new Parent();
Prototype new, 함수를 객체화
Parent의 prototype
Parent
function을 ‘new’ 연산자로 호출하며 어떻게 되는가?
function Parent() {}var oP = new Parent();
이미 존재하는 prototype객체에 접근 할 수 있는 것이 생김.
이것은 [[Prototype]] 라는 이름을 가진 객체임. (내부적으로 숨겨져 있으며 비표준이며 크롬 디버깅 도구로 확인해보면‘__proto__’ 라고 표현함)이것이 위 코드에서 oP에 해당하며, instance라고 함.
oP
[[Prototype]]
Prototype new, 함수를 객체화
Parent의 prototype
ParentParent의
prototypefunction Parent() {}
//새로운 prototype 프로퍼티(이렇게 추가 가능)Parent.prototype.getName= function(){}
var oP = new Parent();
//prototype객체에 접근하기oP.getName;
이제, prototype객체에 있는 프로퍼티를 접근 할 수가 있음
oP
[[Prototype]]
getName (function)
Prototype
function Parent() {}Parent.prototype.getName = function(){}
function Child() {}Child.prototype.getName(){}
//같은 것을 바라봄 (실제로 같은 것이 됨)Child.prototype = new Parent();
prototype은 다른 prototype을 가질 수 있음관계를 거슬로 올라가면 Object 의 prototype 까지 연결되어 있음
결국 chain이 형성되는 구조임
Prototype Chain
Object의 prototype
getName (function)
Child의 prototype
Parent의 prototype
ParentParent의
prototype
function Parent() {}Parent.prototype.getName(){} //3
function Child() {}Child.prototype.getName(){} //2
Child.prototype = Parent.prototype;
var oC = new Child();oC.getName = function(){}; //1
//1 -> 2 -> 3 순으로 탐색oC.getName();
객체에 직접 할당 된 (prototype에 있지 않은) 프로퍼티가 있다면, prototype 객체 내에서 찾는 것보다 앞서서 먼저 찾음
ChildChild의
prototype
3. getName (function)
1. getName (function) 2. getName (function)
Prototype Chain
var A = function() { this.name = "jisu";}
A.prototype.name = "next";
var oa = new A();
oa.name; //”jisu”
var A = function() { this.name = "jisu";}
A.prototype.name = "next";
var oa = new A();
oa.name;
delete oa.name; //인스턴스에 할당된 프로퍼티 삭제
oa.name; //”next”
인스턴스에 할당된 프로퍼티를출력 prototype 객체의 프로퍼티를 출력
Prototype Chain
객체에 직접 할당 된 (prototype에 있지 않은) 프로퍼티가 있다면, prototype 객체 내에서 찾는 것보다 앞서서 먼저 찾음
아래 경우는 동일한 이름을 가지는 프로퍼티를 할당할 때 출력되는 순서임
이렇게 하면 안되나?
function Parent() { this.getName = function(){} this.fixName = “hary”}
var oP = new Parent();
이렇게
이렇게 하면 안되나? 불필요한 함수 생성 비용 문제가 있음
이렇게
getName과 같은 함수가 많아지면,
메모리에 서로 다른 함수가 증가.또한 각 인스턴스마다 프로퍼티가 늘어나는 비용도 함께 발생
새로운 인스턴스가 생길때 마다,
getName()이 서로 다른 함수로 생성 됨
내부에서 new Function()으로 다른 함수가 생성
oP1.getName === oP2.getName //falseoP2.getName === oP3.getName //false
function Parent() { this.getName = function(){} this.fixName = “hary”}
var oP1 = new Parent();var oP2 = new Parent();var oP3 = new Parent();
이렇게 하면 안되나? prototype 은 어떨까?
새로운 인스턴스가 생길때 마다,
getName()이 서로 다른 함수로 생성 됨
내부에서 new Function()으로 다른 함수가 생성
oP1.getName === oP2.getName //falseoP2.getName === oP3.getName //false
이렇게
function Parent() { this.getName = function(){} this.fixName = “hary”}
var oP1 = new Parent();var oP2 = new Parent();var oP3 = new Parent();
이렇게 하면 안되나? 불필요한 함수 생성이 발생하지 않음
function Parent() {}Parent.prototype.getName = function() {}Parent.prototype.fixName = “hary”
var oP1 = new Parent();var oP2 = new Parent();var oP3 = new Parent();
prototype을 활용
versus
새로운 인스턴스가 생성될때마다, getName()이 서로 같은 함수로 공유되며,인스턴스마다 프로퍼티를 따로 가지지 않음
oP3.getName === oP4.getName //true
function Parent() { this.getName = function(){} this.fixName = “hary”}
var oP1 = new Parent();var oP2 = new Parent();var oP3 = new Parent();
새로운 인스턴스가 생길때 마다,
getName()이 서로 다른 함수로 생성 됨
내부에서 new Function()으로 다른 함수가 생성
oP1.getName === oP2.getName //falseoP2.getName === oP3.getName //false
이렇게
이렇게 하면 안되나? 불필요한 함수 생성이 발생하지 않음
function Parent() {}var oP1 = new Parent();Parent.prototype.getName = function() {}Parent.prototype.fixName = “hary”
prototype을 활용
var oP2 = new Parent();var oP3 = new Parent();
새로운 인스턴스가 생성될때마다, getName()이 서로 같은 함수로 공유되며,인스턴스마다 프로퍼티를 따로 가지지 않음
oP3.getName === oP4.getName //true
Parent
oP1
Parent의 prototype
[[Prototype]]
getName (function)
fixName (string)
oP2
oP3
결국,prototype chain을 통한 메소드와 같은 프로퍼티 생성은,불필요한 함수내의 scope chain을 찾지 않고, prototype chain만을 상대적으로 적은 비용임
그런데 좀전 코드에서,, 공유가 된다고?
function Parent() {}var oP1 = new Parent();Parent.prototype.getName = function() {}Parent.prototype.fixName = “hary”
var oP2 = new Parent();var oP3 = new Parent();var oP4 = new Parent();......
새로운 인스턴스가 생성될때마다, getName()이 서로 같은 함수로 공유 됨
oP2.getName === oP3.getName //true
prototype을 활용
그래서, 이런 경우는 주의
function Parent() {}var oP1 = new Parent();Parent.prototype.getName = function() {}Parent.prototype.fixName = “hary”Parent.prototype.aFamily = {father:”jisu” , uncle:”bongbong”}
prototype을 활용
var oP2 = new Parent();var oP3 = new Parent();
//father 의 value를 변경oP2.aFamily.father = “Michael”;
//father 의 value가 변경됐음을 확인oP2.aFamily[‘father’]; //Michael;
//그런데 oP3도 변경이 됐음oP3.aFamily[‘father’]; // ?
‘Michael’이 출력됨
function Parent() {}Parent.prototype.getName = function(){return 123};
var oP = new Parent();
function Child() {}
Child.prototype = new Parent();
var oC = new Child();
Child.prototype.getName = function(){return 456};
console.log(oC.getName()); // 456console.log(oP.getName()); // 123
부모의 인스턴스를 자식에 연결.
Child.prototype = new Parent();
부모의 prototype 프로퍼티 값이 변경되지 않았음 : )
적절한 상속 방법
그런데 혹시,,prototype을 직접 연결하면 안되는 건가요?
Child.prototype = Parent.prototype;
function Parent() {}Parent.prototype.getName = function(){return 123};
var oP = new Parent();
function Child() {}
Child.prototype = Parent.prototype;
var oC = new Child();
Child.prototype.getName = function(){return 456};
console.log(oC.getName()); // 456console.log(oP.getName()); // 456
부모의 prototype 프로퍼티 값이 변경되었음 : (
적절한 상속 방법
이러한 이유는? 두 개의 prototype객체가 결국 같은 것이 됨으로, 수정시 부모의 prototype객체에 직접 반영되기 때문
Child.prototype = Parent.prototype;
아래의 경우는,prototype객체의 직접참조가 아닌, __proto__([[Prototype]]) 를 통해서 참조된 상태 임으로, 부모의 Prototype내에 정보가 변경되지 않고, 자신의 prototype 객체 영역에만 그 사항이 추가됨
Child.prototype = new Parent();
Child1. __proto__: Parent
1. getName: function (){return 456}2. __proto__: Parent
1. constructor: function Parent() {}2. getName: function (){return 123}3. __proto__: Object
Child1. __proto__: Parent
1. constructor: function Parent() {}2. getName: function (){return 456}3. __proto__: Object
적절한 상속 방법
prototype Chain 관계도로 살펴보기
function Parent() {}Parent.prototype.getName = function(){return 123};
function Child() {}
Child.prototype = new Parent();
var oC = new Child();
Child.prototype.getName = function(){return 456};
console.log(oC.getName()); // 456
ParentParent의
prototypegetName (function)
Child
oC
Child의 prototype
[[Prototype]]
[[Prototype]]
function Parent() {}Parent.prototype.getName = function(){return 123};
function Child() {}
Child.prototype = new Parent();
var oC = new Child();
Child.prototype.getName = function(){return 456};
console.log(oC.getName()); // 456
prototype Chain 관계도로 살펴보기
인스턴스 생성 = new Parent()
ECMAScript 5 에 반영된 Object.create() 함수는 비슷한 기능을 제공.
부모의 생성자를 직접 생성하지 않고 prototype을 직접연결하지만, 부모의 메소드나 프로퍼티가 함부로 변경되지 않음
이렇게 prototype 키워드를 직접 활용한 상속방법을,
Prototypal Inheritance이라고 한다.
Prototypal inheritance지금까지의 방법들
function Parent() {}Parent.prototype.getName(){}
function Child() {}
Child.prototype = Parent.prototype;
var oC = new Child();
oC.getName();
function Parent() {}Parent.prototype.getName(){}
function Child() {}
Child.prototype = new Parent();
var oC = new Child();
oC.getName();
문제Prototype으로 바로 연결되어, 부모의 프로퍼티의 값이 원치 않게 변경되는 경우가 발생
문제부모의 생성자를 호출해야 함
또 다른 문제이렇게 무의미한 부모의 함수를 호출하는 경우 만약, 부모에서 인자(argument)의 갯수를 체크하는 로직이
포함되어 있다면 부모를 호출 할 수 없을 수도 있습니다
Prototypal inheritance새로운 방법
function Parent() {}Parent.prototype.getName(){}
function Child() {}
Child.prototype = Parent.prototype;
var oC = new Child();
oC.getName();
function Parent() {}Parent.prototype.getName(){}
function Child() {}
Child.prototype = new Parent();
var oC = new Child();
oC.getName();
문제Prototype으로 바로 연결되어, 부모의 프로퍼티의 값이 원치 않게 변경되는 경우가 발생
문제부모의 생성자를 호출해야 함
function Parent() {}Parent.prototype.getName(){}
function Child() {}
Child.prototype = Object. create(Parent.prototype);
var oC = new Child();
oC.getName();
prototypal inheritanceprototype으로 연결하되, 부모의 프로퍼티 값이 함부로 변경되는 것을 방지
Prototypal inheritanceObject.create() 내부의 동작은 ?
function create(o) { function F() {} F.prototype = o; return new F();}
대략 이런 모습 (실제 create() 내부 소스는 아님)
Prototypal inheritanceObject.create()
function create(o) { function F() {} F.prototype = o; return new F();}
대략 이런 모습 (실제 create() 내부 소스는 아님)
비어있는 Function한개를 만들고, (1 line)
전달받은 객체를 자신의 prototype로 등록하고, (2 line)
인스턴스를 반환 (3 line)
F()는 마치 proxy와 같은 역할을 하면서 ,부모와 자식간의 직접적인 연결을 끊어,자식에 의하여 부모의 정보가 함부로 수정되는 일을 막는다.
function Parent() {}Parent.prototype.getName(){}
function Child() {}
Child.prototype = Object.create(Parent.prototype);
var oC = new Child();
oC.getName();
Prototypal inheritanceObject.create()
Child
oC
Child의 prototype
function create(o) { function F() {} F.prototype = o; return new F();}
F의 prototype
인스턴스 생성 = new F()
= Parent.prototype
부모를 한 번 호출 해주면 된다
Prototypal inheritance부모의 생성자에서 정의한 프로퍼티를 활용하려면?
function Parent(name) { this.name = name;}
Parent.prototype.getName = function() { return this.name;}
Parent.prototype.fixName = “hary”
function Child(team) { this.team = team; Parent.call(this,”jisu”);}
Child.prototype = Object.create(Parent.prototype);
var oC = new Child();
oC.getName();//”jisu”
Prototypal inheritance미리선언된 자식의 prototype객체에 연결된 프로퍼티를 사용하기
function Parent(name) { this.name = name;}
Parent.prototype.getName = function() { return this.name;}
Parent.prototype.fixName = “hary”
function Child(team) { this.team = team; Parent.call(this,”jisu”);}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.getTeamInfo = function() { return this.name + “\’s team is “ + this.team;}
var oC = new Child();
oC.getName();oC.getTeamInfo();
이렇게 추가
Prototypal inheritance미리선언된 자식의 prototype객체에 연결된 프로퍼티 사용하기
function Parent(name) { this.name = name;}
Parent.prototype.getName = function() { return this.name;}
Parent.prototype.fixName = “hary”
function Child(team) { this.team = team; Parent.call(this,”jisu”);}
Child.prototype.getTeamInfo = function() { return this.name + “\’s team is “ + this.team;}
Child.prototype = Object.create(Parent.prototype);
var oC = new Child();
oC.getName();oC.getTeamInfo();
create 전에 선언된 method를 인식하게 하려면?
Prototypal inheritance미리선언된 자식의 prototype객체에 연결된 프로퍼티 사용하기
function Parent(name) { this.name = name;}
Parent.prototype.getName = function() { return this.name;}
Parent.prototype.fixName = “hary”
function Child(team) { this.team = team; Parent.call(this,”jisu”);}
Child.prototype.getTeamInfo = function() { return this.name + “\’s team is “ + this.team;}
Child.prototype = Object.create(Parent.prototype);
var oC = new Child();
oC.getName();oC.getTeamInfo();
child에 선언된 함수를 임시로 저장한 후, 나중에 다시 추가 한다.
Child.prototype = Object.create(Parent.prototype);tempInstance = Object.create(Parent.prototype);addChildPrototype(Child.prototype, tempInstance,)Child.prototype = tempInstance;
* 잠깐.반대로 자식prototype에 부모의 정보를 하나씩 추가하면 안되나? 이런 경우 자식과 부모가 같은 메소드가 있는 경우 자식의 메소드가 부모의 메소드로 덮어 쓸 수 있으니 주의 해야 함
Prototypal inheritance미리선언된 자식의 prototype객체에 연결된 프로퍼티 사용하기
tempInstance = Object.create(Parent.prototype);addChildPrototype(Child.prototype, tempInstance)Child.prototype = tempInstance;
addChildPrototype함수를 구현해야 하며, Child.prototype의 프로퍼티를 모두 tempInstance 로 복사할 것이다.
이것은 프로퍼티를 복사하는 것으로 (복제) 이와 같이 상속하는 것을 믹스인(mix-in)상속이라고 합니다
참고사이트 : http://www.2ality.com/2012/01/js-inheritance-by-example.html
Prototypal inheritance미리선언된 자식의 prototype객체의 프로퍼티 사용하기
구현해야 할 것
참고사이트 : http://www.2ality.com/2012/01/js-inheritance-by-example.html
구현프로퍼티를 모두 추출하고,추출된 정보로 새로운 객체 프로퍼티를 정의 한다
구현해야 할 것
function addChildPrototype(source, target) { Object.getOwnPropertyNames(source) .forEach(function(propName) { Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName)) }); return target;}
Prototypal inheritance미리선언된 자식의 prototype객체의 프로퍼티를 사용하기
addChildPrototype함수를 구현해야 하며, Child.prototype의 프로퍼티를 모두 tempInstance 로 복사할 것이다.
이것은 일종의 객체의 프로퍼티를 복사하는 것으로 (복제) 이와 같이 상속하는 것을 믹스인(mix-in)상속이라고 합니다
tempInstance = Object.create(Parent.prototype);addChildPrototype(Child.prototype, tempInstance)Child.prototype = tempInstance;
//자식 복사.function addChildPrototype(target, source) { Object.getOwnPropertyNames(source) .forEach(function(propName) { Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName)) }); return target;}
function Parent(name) { this.name = name;}
Parent.prototype.getName = function() { return this.name;}
function Child(team) { this.team = "FC barcelona" Parent.call(this,"jisu");}
Child.prototype.getTeamInfo = function() { return this.name + "\'s team is " + this.team;}
var tempInstance = Object.create(Parent.prototype);var tempInstance = addChildPrototype(Child.prototype, tempInstance); Child.prototype = tempInstance;
//Child instance생성var oC = new Child();oC.getTeamInfo();
부모의 메소드를 재활용 할 수 있고,
부모의 생성자를 활용할 수도 있으며,
자식에 연결된 메소드도 활용할 수 있다
또한, 부모의 메소드를 수정하여도 원래의 부모의 메소드는 변경이 되지 않게 되었다
Prototypal inheritance최종 소스
Object 메소드
hasOwnProperty(object)
프로퍼티 정보를 boolean로 반환 (prototype에 연결된 프로퍼티는 확인하지 않음)
직접 소유한 해당이름의 프로퍼티가 객체에 존재하는지 확인하기 위해 사용
function Parent(x,y) { this.x = x; this.y = y; this.getXPos = function(){ return this.x; }}
Parent.prototype.getYPos = function() { return this.y;}
var oP = new Parent(33,44);
oP.hasOwnProperty(“x”); //trueoP.hasOwnProperty(“getXPos”); //trueoP.hasOwnProperty(“getYPos”); //false
hasOwnProperty.call(oP, “x”); //true
getOwnPropertyNames(object)
직접 가지고 있는 프로퍼티의 이름을 배열로 반환 (prototype에 연결된 프로퍼티는 확인하지 않음)
프로퍼티 정보를 추출하기 위해서 사용한다
function Parent(x,y) { this.x = x; this.y = y; this.getXPos = function(){ return this.x; }}
Parent.prototype.getYPos = function() { return this.y;}
Object.getOwnPropertyNames(new Parent());//["x", "y", "getXPos"]//getYPos는 출력되지 않는다
Object 메소드
getOwnPropertyDescriptor(object, propName)
속성의 Descriptor정보를 객체로 반환
객체로부터 지정한 프로퍼티의 descriptor를 얻기 위해서 사용한다.
function Parent(x,y) { this.x = x; this.y = y; this.getYPos = function(){ return this.y; }}
Parent.prototype.getXPos = function() { return this.x;}
var aProps = Object.getOwnPropertyNames(new Parent());
for(var i = 0 ; i < aProps.length ; i++) { var oDesc = Object.getOwnPropertyDescriptor(aProps, i); console.log(oDesc); ......}
Object 메소드
defineProperty(object, propName, descriptor)
descriptor 특성을 갖는 propName이라는 이름의 속성을 object에 추가하고, object를 반환
객체를 복사에 활용하거나, 객체에 특정 descriptor특징을 갖는 속성을 추가 할 때 사용함
var obj = {}
Object.defineProperty(obj, "name" , { value : "jisu", writable:false });
* descriptor 는 ‘data property’ 와 ‘accessor property’가 있으며 이를 잘 이해해야 함참고: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty
Object 메소드
create(proto, propertiesObject)
proto 객체(object literal 또는 특정 object의 prototype객체)를 받아서 새로운 function instance(object)를 반환이때 propertiesObject를 통해서 object속성을 추가할 수 있음
prototype객체를 상속받을 때 유용하게 사용됨
var obj = {name : “jisu”}
var childobj = Object.create(obj);console.log(childobj.name);//jisu
function Parent(x,y) { this.x = x; this.y = y; this.getXPos = function(){ return this.x; }}
Parent.prototype.getYPos = function() { return this.y;}
function Child(x,y) { Parent.apply(this, arguments);}
Child.prototype = Object.create(Parent.prototype);var oc = new Child();
//Child에서 getYPos를 사용할 수가 있게 됨oc.getYPos();
Object 메소드
End;-D
이����������� ������������������ 문서가����������� ������������������ 처음����������� ������������������ 공개����������� ������������������ 된����������� ������������������ 후,����������� ������������������
오타,문서내����������� ������������������ 설명과����������� ������������������ 소스코드에서의����������� ������������������ 문제를����������� ������������������ 지적해주신����������� ������������������
‘정대선’님����������� ������������������ ,‘옥정수’님����������� ������������������ ‘김준기’님����������� ������������������ 그리고����������� ������������������ AjaxUI랩����������� ������������������ 여러분께����������� ������������������ 감사드려요!