4. object 선언
객체에 프로퍼티(속성)를 추가
var obj = {
name : “jisu”, obj
getName : function() {
return this.name; name
} getName
}
5. object 선언
객체를 사용 (여러번)
만약 obj와 같은 객체가 여러개여야하고, 각 객체가 서로 다른 name을 가져야 한다면?
var obj = {
name : “jisu”, obj
getName : function() {
return this.name; name
} getName
}
obj.getName; //jisu
8. Prototype
모든 function는 prototype 이라는 객체를 가지고 있다
function Parent() {} Parent 의
(‘prototype’ in Parent) == true Parent
prototype
9. Prototype
사실 function뿐 아니라 모든 객체(object) 는 prototype 프로퍼티 객체를 가지고 있음
하지만 주로 function을 객체화 한 후 많이 활용함
var obj = {};
obj.constructor.prototype; //확인하기 1
{}.__proto__; //확인하기 2
10. Prototype
사실 function뿐 아니라 모든 객체(object) 는 prototype을 가지고 있음
하지만 주로 function을 객체화 한 후 많이 활용함
?
> ‘new’ 연산자
12. Prototype
new, 함수를 객체화
function을 ‘new’ 연산자로 호출하며 어떻게 되는가?
function Parent() {} Parent의
Parent
var oP = new Parent(); prototype
[[Prototype]]
oP
이미 존재하는 prototype객체에 접근 할 수 있는 것이 생김.
이것은 [[Prototype]] 라는 이름을 가진 객체임. (내부적으로 숨겨져 있으며 비표준이며 크롬 디버깅 도구로 확
인해보면‘__proto__’ 라고 표현함)
이것이 위 코드에서 oP에 해당하며, instance라고 함.
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. Prototype Chain
prototype은 다른 prototype을 가질 수 있음
Object의
관계를 거슬로 올라가면 Object 의 prototype 까지 연결되어 있음
prototype
결국 chain이 형성되는 구조임
Parent의
prototype
function Parent() {} getName (function)
Parent.prototype.getName = function(){}
function Child() {}
Child.prototype.getName(){}
Child의
//같은 것을 바라봄 (실제로 같은 것이 됨)
Child.prototype = new Parent(); prototype
15. Prototype Chain
객체에 직접 할당 된 (prototype에 있지 않은) 프로퍼티가 있다면,
prototype 객체 내에서 찾는 것보다 앞서서 먼저 찾음
function Parent() {}
Parent의
Parent.prototype.getName(){} //3 Parent
prototype
function Child() {}
3. getName (function)
Child.prototype.getName(){} //2
Child.prototype = Parent.prototype;
var oC = new Child();
oC.getName = function(){}; //1
Child의
//1 -> 2 -> 3 순으로 탐색 Child
oC.getName(); prototype
1. getName (function) 2. getName (function)
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”
18. 이렇게 하면 안되나?
이렇게
function Parent() {
this.getName = function(){}
this.fixName = “hary”
}
var oP = new Parent();
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. 이렇게 하면 안되나?
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. 이렇게 하면 안되나?
불필요한 함수 생성이 발생하지 않음
이렇게 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. 이렇게 하면 안되나?
불필요한 함수 생성이 발생하지 않음
prototype을 활용 Parent의
Parent
function Parent() {} prototype
var 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()이 서로 같은 함수로 공유되며,
인스턴스마다 프로퍼티를 따로 가지지 않음
oP3
oP3.getName === oP4.getName //true
24. 결국,
prototype chain을 통한 메소드와 같은 프
로퍼티 생성은,
불필요한 함수내의 scope chain을 찾지 않
고, prototype chain만을
상대적으로 적은 비용임
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. 그래서, 이런 경우는 주의
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’이 출력됨
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()); // 456
console.log(oP.getName()); // 123 음 :)
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. 적절한 상속 방법
이러한 이유는?
두 개의 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. 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. prototype Chain
관계도로 살펴보기
Parent의
Parent
function Parent() {}
prototype
Parent.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. ECMAScript 5 에 반영된 Object.create() 함수는 비슷한 기능을 제공.
부모의 생성자를 직접 생성하지 않고 prototype을 직접연결하지만,
부모의 메소드나 프로퍼티가 함부로 변경되지 않음
이렇게 prototype 키워드를 직접 활용한 상속방법을,
Prototypal Inheritance
이라고 한다.
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. 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 inheritance
Prototype으로 바로 연결되어, 부모의
부모의 생성자를 호출해야 함 prototype으로 연결하되,
프로퍼티의 값이 원치 않게 변경되는 경
부모의 프로퍼티 값이 함부로 변경되는
우가 발생
것을 방지
41. Prototypal inheritance
Object.create()
대략 이런 모습 (실제 create() 내부 소스는 아님)
function create(o) {
function F() {} 비어있는 Function한개를 만들고, (1 line)
전달받은 객체를 자신의 prototype로 등록하고, (2 line)
F.prototype = o; 인스턴스를 반환 (3 line)
return new F(); F()는 마치 proxy와 같은 역할을 하면서 ,
부모와 자식간의 직접적인 연결을 끊어,
} 자식에 의하여 부모의 정보가 함부로 수정되는 일을 막는다.
42. Prototypal inheritance
Object.create()
function Parent() {} F의
Parent.prototype.getName(){}
prototype
function Child() {}
= Parent.prototype
Child.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. 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. 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. 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. 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. Prototypal inheritance
미리선언된 자식의 prototype객체에 연결된 프로퍼티 사용하기
tempInstance = Object.create(Parent.prototype);
Child.prototype = Object.create(Parent.prototype); addChildPrototype(Child.prototype, tempInstance,)
Child.prototype = tempInstance;
* 잠깐.
반대로 자식prototype에 부모의 정보를 하나씩 추가하면 안되나?
이런 경우 자식과 부모가 같은 메소드가 있는 경우 자식의 메소드가 부모의 메소드로 덮어 쓸 수 있으니 주의 해야 함
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. 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. 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();
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”); //true
oP.hasOwnProperty(“getXPos”); //true
oP.hasOwnProperty(“getYPos”); //false
hasOwnProperty.call(oP, “x”); //true
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. 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. 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. 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();