Javascript prototype & inheritance

3,807 views

Published on

자바스크립트의 prototype과 이를 활용한 상속방법은 어떤 것이 있는지 확인 할 수 있습니다.

Published in: Technology
1 Comment
24 Likes
Statistics
Notes
  • 11월30일 내용 수정해서 다시 올림.

    소스코드 및 내용에서의 문제점을 수정했습니다.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
3,807
On SlideShare
0
From Embeds
0
Number of Embeds
1,004
Actions
Shares
0
Downloads
67
Comments
1
Likes
24
Embeds 0
No embeds

No notes for slide

Javascript prototype & inheritance

  1. 1. JavascriptPrototype & Inheritance윤지수. NEXT
  2. 2. Part1. 기본
  3. 3. object 선언 var obj = {} obj
  4. 4. object 선언객체에 프로퍼티(속성)를 추가 var obj = { name : “jisu”, obj getName : function() { return this.name; name } getName }
  5. 5. object 선언객체를 사용 (여러번) 만약 obj와 같은 객체가 여러개여야하고, 각 객체가 서로 다른 name을 가져야 한다면? var obj = { name : “jisu”, obj getName : function() { return this.name; name } getName } obj.getName; //jisu
  6. 6. function 을 활용.
  7. 7. function 선언 function Parent() {} Parent
  8. 8. Prototype 모든 function는 prototype 이라는 객체를 가지고 있다function Parent() {} Parent 의(‘prototype’ in Parent) == true Parent prototype
  9. 9. Prototype사실 function뿐 아니라 모든 객체(object) 는 prototype 프로퍼티 객체를 가지고 있음하지만 주로 function을 객체화 한 후 많이 활용함var obj = {};obj.constructor.prototype; //확인하기 1{}.__proto__; //확인하기 2
  10. 10. Prototype사실 function뿐 아니라 모든 객체(object) 는 prototype을 가지고 있음하지만 주로 function을 객체화 한 후 많이 활용함?> ‘new’ 연산자
  11. 11. Prototypenew, 함수를 객체화function을 ‘new’ 연산자로 호출하면 어떻게 되는가?function Parent() {} Parent의 Parentvar oP = new Parent(); prototype
  12. 12. Prototypenew, 함수를 객체화function을 ‘new’ 연산자로 호출하며 어떻게 되는가?function Parent() {} Parent의 Parentvar oP = new Parent(); prototype [[Prototype]] oP이미 존재하는 prototype객체에 접근 할 수 있는 것이 생김.이것은 [[Prototype]] 라는 이름을 가진 객체임. (내부적으로 숨겨져 있으며 비표준이며 크롬 디버깅 도구로 확인해보면‘__proto__’ 라고 표현함)이것이 위 코드에서 oP에 해당하며, instance라고 함.
  13. 13. Prototype이제, prototype객체에 있는 프로퍼티를 접근 할 수가 있음 Parent의function Parent() {} Parent prototype//새로운 prototype 프로퍼티(이렇게 추가 가능) getName (function)Parent.prototype.getName= function(){} [[Prototype]]var oP = new Parent(); oP//prototype객체에 접근하기oP.getName;
  14. 14. Prototype Chainprototype은 다른 prototype을 가질 수 있음 Object의관계를 거슬로 올라가면 Object 의 prototype 까지 연결되어 있음 prototype결국 chain이 형성되는 구조임 Parent의 prototypefunction Parent() {} getName (function)Parent.prototype.getName = function(){}function Child() {}Child.prototype.getName(){} Child의//같은 것을 바라봄 (실제로 같은 것이 됨)Child.prototype = new Parent(); prototype
  15. 15. Prototype Chain객체에 직접 할당 된 (prototype에 있지 않은) 프로퍼티가 있다면,prototype 객체 내에서 찾는 것보다 앞서서 먼저 찾음function Parent() {} Parent의Parent.prototype.getName(){} //3 Parent prototypefunction Child() {} 3. getName (function)Child.prototype.getName(){} //2Child.prototype = Parent.prototype;var oC = new Child();oC.getName = function(){}; //1 Child의//1 -> 2 -> 3 순으로 탐색 ChildoC.getName(); prototype 1. getName (function) 2. getName (function)
  16. 16. Prototype Chain객체에 직접 할당 된 (prototype에 있지 않은) 프로퍼티가 있다면,prototype 객체 내에서 찾는 것보다 앞서서 먼저 찾음아래 경우는 동일한 이름을 가지는 프로퍼티를 할당할 때 출력되는 순서임인스턴스에 할당된 프로퍼티를출력 prototype 객체의 프로퍼티를 출력var A = function() { var A = function() { this.name = "jisu"; this.name = "jisu";} }A.prototype.name = "next"; A.prototype.name = "next";var oa = new A(); var oa = new A();oa.name; //”jisu” oa.name; delete oa.name; //인스턴스에 할당된 프로퍼티 삭제 oa.name; //”next”
  17. 17. 그런데,이런식으로 prototype을 사용하는 이유는무엇인가요?
  18. 18. 이렇게 하면 안되나? 이렇게 function Parent() { this.getName = function(){} this.fixName = “hary” } var oP = new Parent();
  19. 19. 이렇게 하면 안되나?불필요한 함수 생성 비용 문제가 있음 이렇게 function Parent() { this.getName = function(){} this.fixName = “hary” } var oP1 = new Parent(); var oP2 = new Parent(); getName과 같은 함수가 많아지면, var oP3 = new Parent(); 메모리에 서로 다른 함수가 증가. 또한 각 인스턴스마다 프로퍼티가 늘어나는 비용도 함께 발생 새로운 인스턴스가 생길때 마다, getName()이 서로 다른 함수로 생성 됨 내부에서 new Function()으로 다른 함수가 생성 oP1.getName === oP2.getName //false oP2.getName === oP3.getName //false
  20. 20. 이렇게 하면 안되나?prototype 은 어떨까? 이렇게 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 //false oP2.getName === oP3.getName //false
  21. 21. 이렇게 하면 안되나?불필요한 함수 생성이 발생하지 않음 이렇게 prototype을 활용 function Parent() { function Parent() {} this.getName = function(){} Parent.prototype.getName = function() {} this.fixName = “hary” Parent.prototype.fixName = “hary” } var oP1 = new Parent(); var oP1 = new Parent(); var oP2 = new Parent(); var oP3 = new Parent(); versus var oP2 = new Parent(); var oP3 = new Parent(); 새로운 인스턴스가 생성될때마다, 새로운 인스턴스가 생길때 마다, getName()이 서로 같은 함수로 공유되며, getName()이 서로 다른 함수로 생성 됨 인스턴스마다 프로퍼티를 따로 가지지 않음 내부에서 new Function()으로 다른 함수가 생성 oP3.getName === oP4.getName //true oP1.getName === oP2.getName //false oP2.getName === oP3.getName //false
  22. 22. 이렇게 하면 안되나?불필요한 함수 생성이 발생하지 않음prototype을 활용 Parent의 Parentfunction Parent() {} prototypevar oP1 = new Parent(); getName (function)Parent.prototype.getName = function() {} fixName (string)Parent.prototype.fixName = “hary” oP1 [[Prototype]]var oP2 = new Parent();var oP3 = new Parent(); oP2새로운 인스턴스가 생성될때마다,getName()이 서로 같은 함수로 공유되며,인스턴스마다 프로퍼티를 따로 가지지 않음 oP3oP3.getName === oP4.getName //true
  23. 23. 결국prototype chain을 통한 탐색이 prototype객체 공유로 인해 비용측면에서 상대적으로 저렴하다.
  24. 24. 결국,prototype chain을 통한 메소드와 같은 프로퍼티 생성은,불필요한 함수내의 scope chain을 찾지 않고, prototype chain만을상대적으로 적은 비용임
  25. 25. 그런데 좀전 코드에서,, 공유가 된다고?prototype을 활용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
  26. 26. 그래서, 이런 경우는 주의prototype을 활용function Parent() {}var oP1 = new Parent();Parent.prototype.getName = function() {}Parent.prototype.fixName = “hary”Parent.prototype.aFamily = {father:”jisu” , uncle:”bongbong”}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’이 출력됨
  27. 27. Part2.상속
  28. 28. [REMIND]prototype 객체 자체는,다른 prototype객체를 가지고 있음: prototype Chain
  29. 29. 크롬 디버깅 도구로 확인prototype 객체 자체는 다른 prototype객체를 가지고 있음 prototype안에 다른 prototype 객체
  30. 30. 그렇다면,prototype Chain을 이용해서,다른 prototype객체를 내 것으로 활용하자상속
  31. 31. 적절한 상속 방법부모의 인스턴스를 자식에 연결.Child.prototype = new Parent();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}; 부모의 prototype 프로퍼티 값이 변경되지 않았console.log(oC.getName()); // 456console.log(oP.getName()); // 123 음 :)
  32. 32. 적절한 상속 방법그런데 혹시,,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()); // 456 부모의 prototype 프로퍼티 값이 변경되었음 : (console.log(oP.getName()); // 456
  33. 33. 적절한 상속 방법이러한 이유는?두 개의 prototype객체가 결국 같은 것이 됨으로, 수정시 부모의 prototype객체에 직접 반영되기 때문Child.prototype = Parent.prototype;Child 1. __proto__: Parent 1. constructor: function Parent() {} 2. getName: function (){return 456} 3. __proto__: Object아래의 경우는,prototype객체의 직접참조가 아닌, __proto__([[Prototype]]) 를 통해서 참조된 상태 임으로, 부모의 Prototype내에 정보가 변경되지 않고, 자신의 prototype 객체 영역에만 그 사항이 추가됨Child.prototype = new Parent();Child 1. __proto__: Parent 1. getName: function (){return 456} 2. __proto__: Parent 1. constructor: function Parent() {} 2. getName: function (){return 123} 3. __proto__: Object
  34. 34. 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
  35. 35. prototype Chain 관계도로 살펴보기 Parent의 Parentfunction Parent() {} prototypeParent.prototype.getName = function(){return 123}; getName (function)function Child() {} [[Prototype]]Child.prototype = new Parent();var oC = new Child();Child.prototype.getName = function(){return 456}; Child의console.log(oC.getName()); // 456 Child prototype 인스턴스 생성 = new Parent() [[Prototype]] oC
  36. 36. ECMAScript 5 에 반영된 Object.create() 함수는 비슷한 기능을 제공.부모의 생성자를 직접 생성하지 않고 prototype을 직접연결하지만,부모의 메소드나 프로퍼티가 함부로 변경되지 않음이렇게 prototype 키워드를 직접 활용한 상속방법을,Prototypal Inheritance이라고 한다.
  37. 37. Part3. Prototypal Inheritance
  38. 38. Prototypal inheritance지금까지의 방법들 function Parent() {} function Parent() {} Parent.prototype.getName(){} Parent.prototype.getName(){} function Child() {} function Child() {} Child.prototype = Parent.prototype; Child.prototype = new Parent(); var oC = new Child(); var oC = new Child(); oC.getName(); oC.getName(); 문제 문제 Prototype으로 바로 연결되어, 부모의 부모의 생성자를 호출해야 함 프로퍼티의 값이 원치 않게 변경되는 경 우가 발생 또 다른 문제 이렇게 무의미한 부모의 함수를 호출하는 경우 만약, 부모에서 인자(argument)의 갯수를 체크하는 로직이 포함되어 있다면 부모를 호출 할 수 없을 수도 있습 니다
  39. 39. Prototypal inheritance 새로운 방법function Parent() {} function Parent() {} function Parent() {}Parent.prototype.getName(){} Parent.prototype.getName(){} Parent.prototype.getName(){}function Child() {} function Child() {} function Child() {}Child.prototype = Parent.prototype; Child.prototype = new Parent(); Child.prototype = Object. create(Parent.prototype);var oC = new Child(); var oC = new Child(); var oC = new Child();oC.getName(); oC.getName(); oC.getName();문제 문제 prototypal inheritancePrototype으로 바로 연결되어, 부모의 부모의 생성자를 호출해야 함 prototype으로 연결하되,프로퍼티의 값이 원치 않게 변경되는 경 부모의 프로퍼티 값이 함부로 변경되는우가 발생 것을 방지
  40. 40. Prototypal inheritanceObject.create() 내부의 동작은 ?대략 이런 모습 (실제 create() 내부 소스는 아님)function create(o) { function F() {} F.prototype = o; return new F();}
  41. 41. Prototypal inheritanceObject.create()대략 이런 모습 (실제 create() 내부 소스는 아님)function create(o) { function F() {} 비어있는 Function한개를 만들고, (1 line) 전달받은 객체를 자신의 prototype로 등록하고, (2 line) F.prototype = o; 인스턴스를 반환 (3 line) return new F(); F()는 마치 proxy와 같은 역할을 하면서 , 부모와 자식간의 직접적인 연결을 끊어,} 자식에 의하여 부모의 정보가 함부로 수정되는 일을 막는다.
  42. 42. Prototypal inheritance Object.create()function Parent() {} F의Parent.prototype.getName(){} prototypefunction Child() {} = Parent.prototypeChild.prototype = Object.create(Parent.prototype);var oC = new Child();oC.getName();function create(o) { function F() {} Child의 F.prototype = o; Child return new F(); prototype} 인스턴스 생성 = new F() oC
  43. 43. 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”
  44. 44. 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();
  45. 45. 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;} create 전에 선언된 method를 인식하게 하려면?Child.prototype = Object.create(Parent.prototype);var oC = new Child();oC.getName();oC.getTeamInfo();
  46. 46. Prototypal inheritance미리선언된 자식의 prototype객체에 연결된 프로퍼티 사용하기function Parent(name) { this.name = name;}Parent.prototype.getName = function() { return this.name;}Parent.prototype.fixName = “hary” child에 선언된 함수를 임시로 저장한 후,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();
  47. 47. Prototypal inheritance미리선언된 자식의 prototype객체에 연결된 프로퍼티 사용하기 tempInstance = Object.create(Parent.prototype);Child.prototype = Object.create(Parent.prototype); addChildPrototype(Child.prototype, tempInstance,) Child.prototype = tempInstance;* 잠깐.반대로 자식prototype에 부모의 정보를 하나씩 추가하면 안되나?이런 경우 자식과 부모가 같은 메소드가 있는 경우 자식의 메소드가 부모의 메소드로 덮어 쓸 수 있으니 주의 해야 함
  48. 48. 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
  49. 49. Prototypal inheritance미리선언된 자식의 prototype객체의 프로퍼티를 사용하기구현해야 할 것tempInstance = Object.create(Parent.prototype);addChildPrototype(Child.prototype, tempInstance)Child.prototype = tempInstance;addChildPrototype함수를 구현해야 하며, Child.prototype의 프로퍼티를 모두 tempInstance 로 복사할 것이다.이것은 일종의 객체의 프로퍼티를 복사하는 것으로 (복제) 이와 같이 상속하는 것을 믹스인(mix-in)상속이라고 합니다구현프로퍼티를 모두 추출하고,추출된 정보로 새로운 객체 프로퍼티를 정의 한다function addChildPrototype(source, target) { Object.getOwnPropertyNames(source) .forEach(function(propName) { Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName)) }); return target;} 참고사이트 : http://www.2ality.com/2012/01/js-inheritance-by-example.html
  50. 50. Prototypal inheritance최종 소스//자식 복사.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();
  51. 51. Part4. (참고) native Object methods
  52. 52. 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”); //falsehasOwnProperty.call(oP, “x”); //true
  53. 53. Object 메소드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는 출력되지 않는다
  54. 54. 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); ......}
  55. 55. 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
  56. 56. Object 메소드create(proto, propertiesObject)proto 객체(object literal 또는 특정 object의 prototype객체)를 받아서 새로운 function instance(object)를 반환이때 propertiesObject를 통해서 object속성을 추가할 수 있음prototype객체를 상속받을 때 유용하게 사용됨 function Parent(x,y) {var obj = {name : “jisu”} this.x = x; this.y = y;var childobj = Object.create(obj); this.getXPos = function(){console.log(childobj.name); return this.x;//jisu } } 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();
  57. 57.
  58. 58.  문서가
  59. 59.  처음
  60. 60.  공개
  61. 61.  된
  62. 62.  후,
  63. 63.  오타,문서내
  64. 64.  설명과
  65. 65.  소스코드에서의
  66. 66.  문제를
  67. 67.  지적해주신
  68. 68.  ‘정대선’님
  69. 69.  ,‘옥정수’님
  70. 70.  ‘김준기’님
  71. 71.  그리고
  72. 72.  AjaxUI랩
  73. 73.  여러분께
  74. 74.  감사드려요!End;-D

×