57
Javascript Prototype & Inheritance 윤지수. NEXT

Javascript prototype & inheritance

  • Upload
    -

  • View
    2.653

  • Download
    2

Embed Size (px)

DESCRIPTION

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

Citation preview

JavascriptPrototype & Inheritance윤지수. NEXT

Part1. 기본

objvar obj = {}

object 선언

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을 가져야 한다면?

function 을 활용.

Parentfunction Parent() {}

function 선언

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 객체 내에서 찾는 것보다 앞서서 먼저 찾음

아래 경우는 동일한 이름을 가지는 프로퍼티를 할당할 때 출력되는 순서임

그런데,

이런식으로 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을 통한 탐색이 prototype객체 공유로 인해 비용측면에서 상대적으로 저렴하다.

결국,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’이 출력됨

Part2.상속

[REMIND]

prototype 객체 자체는, 다른 prototype객체를 가지고 있음: prototype Chain

prototype안에 다른 prototype 객체

크롬 디버깅 도구로 확인prototype 객체 자체는 다른 prototype객체를 가지고 있음

그렇다면,

prototype Chain을 이용해서, 다른 prototype객체를 내 것으로 활용하자

상속

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이라고 한다.

Part3. 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최종 소스

Part4. (참고) native Object methods

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랩����������� ������������������  여러분께����������� ������������������  감사드려요!