다루는 내용
- 객체 프로퍼티의 이해
- 객체의 이해와 생성
- 상속의 이해
1. 객체 프로퍼티의 이해
객체는 특별한 순서가 없는 값의 배열 (key-value 쌍의 그룹)
가장 단순한 방법은
var person = new Object(); person.name = "snack"; person.sayName = function() { console.log(this.name) }; var person = { name : 'snack', sayName: function() { console.log(this.name); } };
프로퍼티에는 데이터 프로퍼티와 접근자 프로퍼티 두 가지가 있음.
데이터 프로퍼티에는
- [[Configurable]] - 프로퍼티의 속성을 바꾸거나 접근자 프로퍼티로 변환할 수 있음을 의미 (default : true)
- [[Enumerable]] - for-in 루프에서 해당 프로퍼티를 반환함을 나타냄 (default : true)
- [[Writable]] - 프로퍼티의 값을 바꿀 수 있음을 나타냄 (default : true)
- [[Value]] - 프로퍼티의 실제 데이터 값을 포함 (default : undefined)
var person = {}; Object.defineProperty(person, "name", { writable: false, value: "snack" }); console.log(person.name); // snack person.name = "gray"; console.log(person.name); // snack
접근자 프로퍼티는
- [[Configurable]] - 프로퍼티의 속성을 바꾸거나 데이터 프로퍼티로 변환할 수 있음
- [[Enumerable]] - for-in 루프에서 해당 프로퍼티를 반환함을 나타냄
- [[Get]] - 프로퍼티를 읽을 때 호출 (default : undefined)
- [[Set]] - 프로퍼티를 바꿀 때 호출 (default : undefined)
var book = { _year : 2004, edition : 1 }; Object.defineProperty(book, "year", { get: function() { return this._year; }, set: function(newValue) { if(newValue > 2004) { this._year = newValue; this.edition = this.edition + newValue - 2004; } } }); book.year = 2005; console.log(book.edition); // 2
다중 프로퍼티를 사용할 경우에는 Object.defineProperties() 를 사용
각 프로퍼티의 속성값을 알고 싶은 경우 Object.getOwnPropertyDescription() 을 사용
2. 객체의 이해와 생성
팩토리 패턴 : 특정 객체를 생성하는 과정을 추상화 하는 것
function createPerson(name) { var o = new Object(); o.name = name; return o; } var person1 = createPerson('snack'); var person2 = createPerson('gray'); // 코드의 중복은 해결했으나, 객체가 어떤 타입인지는 알 수 없음
생성자 패턴
function Person(name) { this.name = name; } var person1 = new Person('snack'); var person2 = new Person('gray');
생성자 패턴의 주요 문제는 인스턴스마다 메소드가 생성된다는 점 (함수 이름이 같아도 인스턴스가 다르면 다른 함수로 취급)
프로토 타입 패턴
function Person() { } Person.prototype.name = "snack"; Person.prototype.sayName = function() { console.log(this.name); } var person1 = new Person(); person1.sayName(); // snack var person2 = new Person(); person2.sayName(); // snack console.log(person1.sayName == person2.sayName); // true
프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면 인스턴스에 추가될 뿐 프로토타입까지 올라가지 않음
function Person() { } Person.prototype.name = "snack"; Person.prototype.sayName = function() { console.log(this.name); } var person1 = new Person(); var person2 = new Person(); person.name = "gray"; console.log(person1.name); // gray - 인스턴스 console.log(person2.name); // snack - 프로토타입
hasOwnProperty() 를 통해 인스턴스의 프로퍼티인지 프로토타입의 프로퍼티인지 확인 가능
프로토 타입은 리터럴로 생성이 가능
function Persion() { } Person.prototype = { // constructor : Person - 생성자 타입이 중요하다면 명시적으로 설정 가능 name : "snack", sayName: function() { console.log(this.name); } };
인스턴스는 프로토타입을 가리키는 포인터를 가질 뿐 생성자와 연결된 것은 아니기에, 프로토타입을 다른 객체로 바꾸면 연결이 깨짐
네이티브 타입(Object, Array 등)의 메서드 역시 생성자의 프로토타입에 정의 (ex:Array.prototype.sort)
프로토 타입에 존재하는 프로퍼티는 모두 인스턴스에 공유되는데, 참조 값을 포함하게 될 경우 문제가 생김
function Person() { } Person.prototype = { constrouctor : Person, name: "snack", friends : ["gray", "berry"] } var person1 = new Person(); var person2 = new Person(); person1.friends.push("cheese"); console.log(person1.friends); // "gray,berry,cheese" console.log(person2.friends); // "gray,berry,cheese"
생성자와 프로토타입 패턴의 장점만 취한 패턴
function Person(name) { this.name = name; thus.friends = ["cheese", "berry"] } Person.prototype = { constructor: Person, sayName: function() { console.log(this.name); } } var person1 = new Person("snack"); var person2 = new Person("muz"); person1.friends.push("cake"); console.log(person1.friends); // cheese, berry, cake console.log(person2.friends); // cheese, berry
프로퍼티에 직접 접근할 수 없게 하는 durable 생성자 패턴이 있음(캡슐화?)
function Person(name) { var o = new Object(); o.sayName = function() { console.log(name); }; return o; }
3. 상속의 이해
함수 시그너쳐가 없으므로 인터페이스 상속은 불가능하고, 구현 상속만 지원 (대게 프로토타입 체인)
프로토 타입 체인
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; }; function SubType() { this.subproperty = false; } //SuperType 상속 SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function() { return this.subproperty; }; var instance = new SubType(); console.log(instance.getSuperValue()); // true
모든 참조 타입은 기본적으로 프로토 타입 체인을 통해 Object 를 상속
프로토타입 체인 상속 역시 참조값을 포함한 경우 문제가 될 수 있음
function SuperType() { this.colors = ["red","green","blue"]; } function SubType() { } SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.color.push("black"); console.log(instance1.colors); // red, green, blue, black var instance2 = new SubType; console.log(instance2.colors); // red, green, blue, black
참조 값에 얽힌 문제를 해결하고자 'constructor stealing'(생성자 훔치기) 를 사용
function SuperType() { this.colors = ["red","green"]; } function SubType() { SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); // red, green, black var instance2 = new SubType(); console.log(instance2.colors); // red, green
생성자 훔치기의 경우, 메소드를 생성자 내부에서만 정의해야 하므로 함수 재사용이 불가능. 또한 상위 타입에서 정의된 메소드는 하위 타입에서 접근 불가
프로토 타입 체인과 생성자 훔치기 패턴을 조합한 조합 상속이 있음
function SuperType(name) { this.name = name; this.colors = ["red", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); } function SubType(name, age) { SuperType.call(this, name); this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.sayAge = function() { console.log(this.age); } var instance1 = new SubType("snack", 29); instance1.colors.push("black"); console.log(instance1.colors); // red, green, black instance1.sayName(); // snack instance1.sayAge(); // 29 var instance2 = new SubType("gray", 28); console.log(instance2.colors); // red, green instance2.sayName(); // gray instance2.sayAge(); // 28
프로토 타입 상속
var person = { name: "snack" }; var anotherPerson = object(person); anotherPerson.name = "gray" // EcmaScript 5 var anotherPerson2 = Object.create(person); anthoerPerson2.name = "chees";
기생 상속
function createAnother(original) { var clone = object(original); clone.sayHi = function() { console.log("hi"); }; return clone; } var person = { name : "snack" }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); // hi
조합상속은 자주 쓰이는 상속 패턴이지만, 상위 타입 생성자가 항상 두번 호출된다는 비효율적인 면도 있음
기생 상속 조합은 생성자 훔치기를 통해 프로퍼티 상속을 구현하지만, 메소드 상속에는 프로토타입 체인을 혼용
function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); // 객체 생성 prototype.constructor = subType; // 객체 확장 subType.prototype = prototype; // 객체 할당 } function SuperType(name) { this.name = name; this.colors = ["red", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function() { console.log(this.age); };
'개발 관련 > javascript' 카테고리의 다른 글
8. 브라우저 객체 모델 (0) | 2022.07.13 |
---|---|
7. 함수 표현식 (0) | 2022.07.13 |
5. 참조 타입 (0) | 2022.07.13 |
4. 변수, 스코프, 메모리 (0) | 2022.07.13 |
3. 언어의 기초 (0) | 2022.07.13 |