본문 바로가기

프로그래밍/자바스크립트(javascript)

[자바스크립트/javascript] 객체 (Object) , 프로퍼티, 팩터리 패턴, 생성자 패턴, 프로토타입 패턴, 기생 생성자 패턴, 방탄

자바스크립트는 객체를 "프로퍼티의 순서 없는 컬렉션이며 각 프로퍼티는 원시 값이나 객체, 함수를 포함한다"라고 정의합니다.

객체가 측별한 순서가 없는 값의 배열이란 의미 입니다.

 

객체를 만드는 가장 단순한 방법은 Object의 인스턴스를 만들고 여기에 프로퍼티와 메서드를 추가하는 방법이다.

 

var person = new Object();
person.name = "Nick";
person.age = 29;
person.job = "software Engineer";

person.sayHo = function() {
	console.log(this.name);
}

 

person 이라는 객체를 만들고 name, age, job 세가지 프로퍼티가 있고 sayHo() 이란 메서드가 있습니다.

초기 자바사크립트 개발자들은 객체를 생성할 때 이런 패턴을 자주 사용했습니다. 

 

하지만, 요즘은 객체 리터럴 패턴을 더 많이 사용합니다.

 

var person = {
	name : "Nick",
    age : 29,
    job : "software Engineer",
    
    sayHo : function() {
    	console.log(this.name);
    }
};

 

프로퍼티 타입

 

자바스크립트에서는 프로퍼티의 특징을 내부적으로만 유효한 속성에 따라 설명합니다.

자바스크립트 엔진 내부에서 구현하는 것으로 정의했으므로 이들 속성을 자바스크립트에서 직접적으로 접근할 수 있는 방법은 없습니다.

[] 처럼 속성 이름을 대괄호로 감싸서 내부 속성임을 나타냅니다.

 

프로퍼티를 추가하거나 수정할 객체, 프로퍼티 이름, 서술자 객체 세 가 지를 매개변수를 받습니다.

 

var person = {};
Object.defineProperty(person, "name", {
	writable: false,
    value: "Nick"
});

console.log(person.name);
person.name = "Greg";
console.log(person.name);

 

 

 

 

 

name이라는 프로퍼티를 만들고 "Nick"로 지정합니다. 이 프로퍼티 값은 바꿀 수 없으며, 새로운 값을 할당하려 시도하면 스트릭트 모드가 아닐 때는 무시되고 스트릭트 모드에서는 에러가 발생합니다.

 

var person = {};

Object.defineProperty(person, "name", {
	configurable: false,
    value: "Nick"
});

console.log(person.name);
delete person.name;
console.log(person.name);

 

 

 

 

configurable을 false로 설정하면 해당 프로퍼티를 객체에서 제거할 수 없게 됩니다.

 

var person = {};

Object.defineProperty(person, "name", {
	configurable: false,
    value: "Nick"
});

// 에러 
Object.defineProperty(person, "name", {
	configurable: true,
    value: "Nick"
});

 

 

 

 

 

같은 프로퍼티에서 Obejct.defineProperty() 를 여러 번 호출할 수는 있지만, 일단 configurable을 false로 지정하면 제한이 생깁니다.

object.defineProperty() 처럼 강력한 옵션을 사용할 일이 드물겠지만 자바스크립트 객체를 잘 이해하려면 이 개념에 대해 알아야 합니다.

 

 

접근자 프로퍼티

 

접근자 프로퍼티에는 데이터 값이 들어 있지 않고 대신 getter 함수와 setter 함수로 구성됩니다. 접근자 프로퍼티를 읽을 때는 getter함수가 호출되며 유효한 값을 반환한 책임은 이 함수에 있습니다.

 

접근자 프로퍼티를 명시적으로 정의할 수 없으며 반드시 Object.defineProperty()를 써야 합니다.

 

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 += newValue - 2004;
        }
    }
});

book.year = 2004;
console.log(book.edition);

 

 

 

 

객체 book은 _year와 edition 두 기본 프로퍼티를 가진 채로 생성됩니다.

(_) 밑줄은 객체의 메서드를 통해서만 접근할 것이고 객체 외부에서는 접근하지 않겠다는 의도를 나타낼 때 흔히 쓰는 표기법입니다.

getter 함수는 단순히 _year의 값을 반환하며 setter 함수는 올바른 판을 반환하기 위해 계산을 하며, 값을 넣습니다.

 

getter와 setter는 꼭 지정해야 하는 것은 아닙니다.

getter 함수만 지정하면 해당 프로퍼티는 읽기 전용이 되고 이 프로퍼티를 수정하려는 시도는 모두 무시됩니다. 마찬가지로 setter만 지정된 프로퍼티를 읽으려 하면 스트릭트 모드가 아닐 때는 undefined를 반환하며 스트릭트 모드에는 에러가 발생합니다.

 

예전 브라우저에서는 __defineGetter__() 와 __defineSetter__() 를 이용해 접근자 프로퍼티를 생성했습니다.

 

var book = {
	_year : 2004,
    edition: 1
};

book.__defineGetter__("year", function() {
	return this._year;
});

book.__defineSetter__("year", function(newValue) {
	if( newValue > 2004 ) {
    	this._year = newValue;
        this.edtion += newValue - 2004;
    }
});

book.year = 2005;
console.log(book.edition);

 

 

 

 

 

다중 프로퍼티 정의

 

 

객체에서 프로퍼티 여러 개를 동시에 수정해야 할 가능성이 높으므로 자바스크립트에서는 Object.defineProperties() 메서드를 제공합니다.

이 메서드를 이용하여 여러 프로퍼티를 한번에 여러 개를 한 번에 정의할 수 있습니다.

 

 

var book = {};

Object.definePropertues(book, {
	
    _year: {
    	value: 2004
    },
    
    edition: {
    	value: 1
    },
    
    year: {
    	get: function() {
        	return this._year;
        },
        
        set: function(newValue) {
        	if( newValue > 2004) {
            	this._year = newValue;
                this.edition += newValue - 2004;
            }
        }
    }
});

 

book 객체에 _year와 edition 두 가지 데이터 프로퍼티와 접근자 프로퍼티 year를 생성합니다.

위에 예제와 다른 차이는 여러 프로퍼티를 동시에 생성했다는 것 뿐입니다.

 

접근자 프로퍼티에서는 configurable과 enumerable, get, set 을 프로퍼티로 포함하는 객체를 반환하며 데이터 프로퍼티로 포함하는 개체를 반환합니다.

 

var book = {};

Object.defineProperties(book, {
	
    _year: {
    	value: 2004
    },
    
    edition: {
    	value: 1
    },
    
    year: {
    	get: function() {
        	return this._year;
        },
        
        set: function(newValue) {
        	if( newValue > 2004) {
            	this._year = newValue;
                this.edition += newValue - 2004;
            }
        }
    }
});

var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
console.log(descriptor.value);
console.log(descriptor.configurable);
console.log(typeof descriptor.get);

var descriptor = Object.getOwnPropertyDescriptor(book, "year");
console.log(descriptor.value);
console.log(descriptor.configurable);
console.log(typeof descriptor.get);

 

 

 

 

 

_year 데이터 프로퍼티는 value는 원래 값과 같고 configurable은 flase이며 get은 undefined입니다. 접근자 프로퍼티인 year에서는 value는 undefineed이고 enumerable은 false이며 get은 getter 함수를 가리키는 포인터입니다.

 

 

Object 생성자를 이용하거나 객체 리터럴을 이요해 객체를 생성하면 객체 하나를 생성할 때는 편리하지만, 분명한 단점이 하나 있습니다.

같은 인터페이스를 가진 객체를 여러 개 만들 때는 중복된 코드를 매우 많이 써야 한다는 것이 있습니다.

 

 

팩터리 패턴

 

팩터리 패턴은 특정 객체를 생성하는 과정을 추상화 하는 것으로 잘 알려진 디자인 패턴입니다.

 

function createPerson(name, age, job) {
	var o = new object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayHo = function() {
    	console.log(this.name);
    };
    return o;
}

var person1 = createPerson("Nick", 29, "software Engineer");
var person2 = createPerson("Greg", 27, "Deginer");

 

객체를 만드는 데 필요한 정보를 매개변수로 받아 객체를 생성합니다.

팩터리 패턴을 쓰면 비슷한 객체를 여러 개 만들 때의 코드 중복 문제는 해결할 수 있지만 생성한 객체가 어떤 타입인지 알 수 가 없다는 문제가 있습니다.

 

 

생성자 패턴

 

특정한 타입의 객체를 만드는데 쓰입니다. Object와 Array는 실행 환경에서 자도응로 만들어지는 네이티브 생성자 입니다. 반면 생성자를 만들때 원하는 타입의 객체에 필요한 프로퍼티와 메서드를 직접 정의 할 수 있습니다.

 

function Person(name, age, job) {
	this.name = name;
    this.age = age;
    this.job = job;
    this.sayHo = function() {
    	console.log(this.name);
    };
}

var person1 = new Person("Nick", 29, "Sfotware Engineer");
var person2 = new Person("Greg", 27, "Designer");

 

함수 Person의 이름 첫 글자가 대문자 'P'인 점도 눈여겨 보십시오. 생성자 함수는 항상 대문자로 시작하고 생성자 아닌 함수는 소문자로 시작하는 표기법이 널리 쓰입니다.

 

 

function Person(name, age, job) {
	this.name = name;
    this.age = age;
    this.job = job;
    this.sayHo = function() {
    	console.log(this.name);
    };
}

var person1 = new Person("Nick", 29, "Sfotware Engineer");
var person2 = new Person("Greg", 27, "Designer");

console.log(person1.constructor == Person);
console.log(person1.constructor == Person);

console.log(person1 instanceof Object);
console.log(person1 instanceof Person);

 

 

 

 

만들어준 person1과 person2는 Person의 각각 다른 인스턴스로 채워집니다. constructor 프로퍼티는 다음과 같이 Person을 가리킵니다.

 

constructor 프로퍼티는 원래 객체의 타입을 파악하려는 의도였습니다. 하지만 타입을 알아내는 목적으로는 instanceof 연산자가 더 안전한 것으로 간주됩니다.

 

생성자를 직접 만들면 인스턴스 타입을 쉽게 식별할 수 있는데, 팩터리 패턴에 비해 대단한 장점입니다.

person1과 person2는 모두 Object의 인스턴스인데 커스텀 객체는 모두 Object를 상속하기 때문입니다.

 

 

function Person(name, age, job) {
	this.name = name;
    this.age = age;
    this.job = job;
    this.sayHo = function() {
    	console.log(this.name);
    };
}

var person = new Person("Nick", 29, "Software Engineer");
person.sayHo();

Person("Greg", 27, "Doctor");
window.sayHo();

var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayHo();

 

 

 

 

new 연산자와 함께 사용해서 새로운 객체를 생성했습니다. 두 번째 부분은 Person() 함수를 new 연산자 없이 호추한 경우 입니다.

프로퍼티와 메서드는 window객체에 추가됩니다. 함수를 호출할 때 객체의 메서드로서 호출하거나 call() / apply() 를 통해 호출해서 this의 값을 명시적으로 지정하지 않는다면 this 객체는 항상 Global 객체에 묶입니다.

 

생성자 패러다임은 매우 유용하긴 하지만 단점도 있습니다. 주요 문제는 인스턴스마다 메서드가 생성된다는 점입니다.

sayHo() 라는 메서드가 있지만 이들 메서드는 Function의 같은 인스턴스는 아닙니다.

 

function Person(name, age, job) {
	this.name = name;
    this.age = age;
    this.job = job;
    this.sayHo = new Function("console.log(this.name)");
}

 

이 예제는 Function의 새 인스턴스를 만드는 방식은 위와 같습니다. 함수 이름이 같더라도 인스턴스가 다르면 둘은 다른 함수입니다.

 

 

function Person(name, age, job) {
	this.name = name;
    this.age = age;
    this.job = job;
    this.sayHo = new Function("console.log(this.name)");
}

var person1 = new Person("Nick", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Deginer");

console.log(person1.sayHo == person2.sayHo);

 

 

 

 

 

똑같은 일을 하는 Function 인스턴스 두 개가 따로 존재 한다는 점은 상식적이지 않는데, this 객체를 응용하여 함수가 런타임에서야 비로소 객체에 묶이도록 할 수 있으므로 더 비효율적입니다.

 

 

function Person(name, age, job) {
	this.name = name;
    this.age = age;
    this.job = job;
    this.sayHo = sayHo;
}

function sayHo() {
	console.log(this.name);
}

var person1 = new Person("Nick", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Deginer");

 

sayHo() 함수를 생성자 바깥에서 생성하여 생성자 내부에서 함수를 할당합니다. 이렇게 하면 함수 중복 문제를 막을 수 있다.

일브 객체에서만 쓰이는 함수를 전역에 놓음으로서 전역 스코프를 막을 수 있지만, 일부 객체에서만 쓰이는 함수를 전역에 놓음으로서 전역 스코프를 어지럽힌다는 단점이 있습니다.

 

 

프로토타입 패턴

 

 

모든 함수는 prototype 프로퍼티를 가집니다. 이 프로퍼티는 해당 참조 타입의 인스턴스가 가져야 할 프로퍼티와 메서드를 담고 있는 객체입니다.

 

function Person() {
}

Person.prototype.name = "Nick";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayHo = function() {
	console.log(this.name);
};

var person1 = new Person();
person1.sayHo();

var person2 = new Person();
person2.sayHo();

console.log(person1.sayHo == person2.sayHo);

 

 

 

 

 

객체 인스턴스에서 프로포타입에 있는 값을 읽을 수는 있지만 수정은 불가능합니다. 프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면 해당 프로퍼티는 인스턴스에 추가되며 프로토타입까지 올라가지 않습니다.

 

 

function Person() {
}

Person.prototype.name = "Nick";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";

var person1 = new Person();
var person2 = new Person();

person1.name = "Greg";
console.log(person1.name);
console.log(person2.name);

 

 

 

 

person1의 name의 프로퍼티에 새값이 반영되었습니다.  person1.name 에 접근하면 인스턴스에서 name이라는 프로퍼티를 검색합니다. 프로퍼티가 존재하므로 프로토타입까지 검색하지는 않습니다. 같은 방식으로 person2.name 에 접근하면 인스턴스에서 찾지 못하므로 프로퍼티를 검색해 name 프로퍼티를 찾습니다.

 

 

인스턴스에 프로퍼티가 있으면 프로포타입에 존재하는 같은 이름의 프로퍼티에 대한 접근은 차답합니다. null로 설정해도 인스턴스의 프로퍼티만 변경할 뿐 프로포타입 프로퍼티에 다시 연결 되지 않습니다. 하지만 delete 연산자는 인스턴스 프로퍼티를 완전히 삭제하면 다시 prototype 프로퍼티에 다시 접근 할 수 있습니다.

 

 

function Person() {
}

Person.prototype.name = "Nick";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";

var person1 = new Person();
var person2 = new Person();

person1.name = "Greg";
console.log(person1.name);
console.log(person2.name);

delete person1.name;
console.log(person1.name);

 

 

 

 

hasOwnProperty() 메서드는 프로퍼티가 인스턴스에 존재하는지 프로토 타입에 존재하는 확인합니다.

 

function Person() {
}

Person.prototype.name = "Nick";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";

var person1 = new Person();

console.log(person1.hasOwnProperty("name"));

person1.name = "Greg";
console.log(person1.name);

console.log(person1.hasOwnProperty("name"));

 

 

 

 

hasOwnproperty()를 추가함으로서 인스턴스의 프로퍼티에 접근하는지 프로토타입의 프로퍼티에 접근하는지 명확히 알 수 있습니다.

 

 

in 연산자를 자체적으로 사용하면 주어진 이름의 프로퍼티를 객체에 접근할 수 있을 때, 다시 말해 해당 프로퍼티가 인스턴스에 존재하든 프로토타입에 존재하든 모두 true를 반환합니다.

 

 

function Person() {
}

Person.prototype.name = "Nick";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";

var person1 = new Person();
console.log("name" in person1);

person1.name = "Greg";
console.log("name" in person1);

 

 

 

 

프로퍼티가 인스턴스에 존재하지 않아도 true를 반환합니다.

 

 

function Person() {
}

Person.prototype.name = "Nick";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";

var person = new Person();

function hasPrototypeProperty(object, name) {
    return !object.hasOwnProperty(name) && (name in object);
}

console.log(hasPrototypeProperty(person, "name"));

person.name = "Greg";
console.log(hasPrototypeProperty(person, "name"));

 

 

 

 

 

hasPrototypeProperty()는 name 프로퍼티가 존재하면 true를 반환합니다. name 프로퍼티를 덮어쓰는 순간 이 프로퍼티는 인스턴스에 존재하므로 hasPrototypeProperty()는 false를 반환합니다.

 

 

 

Object.keys() 메서드를 통해 객체 인스턴스에 나열 가능한 프로퍼티의 전체 목록을 얻을 수 있습니다. Obejct.keys() 메서드는 객체를 매개변수로 받은 다음 나열 가능한 프로퍼티 이름을 문자열로 포함하는 배열을 반환합니다.

 

function Person() {
}

Person.prototype.name = "Nick";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";

var keys = Object.keys(Person.prototype);
console.log(keys);

var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);

console.log(p1keys);

 

 

 

 

객체를 다시 호출 하면 인스턴스 프로퍼티 name 과 age만 들어 있는 배열을 반환합니다.

 

나열 가능 여부와 관계 없이 인스턴스 프로퍼티 전체 목록을 얻으려면 Object, getOwnPropertyNames() 메서드 같은 방법으로 사용합니다.

 

function Person() {
}

Person.prototype.name = "Nick";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";

var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);

 

 

 

 

나열 불가능한 constructor 프로퍼티가 결과 목록에 들어있습니다.

 

 

이전에는 프로퍼티와 메서드마다 기입을 해서 썼지만 객체 리터럴로 프로토타입을 덮어써서 반복을 줄이고 프로토타입에 기능을 더 가독성 있게 캡슐화하는 패턴이 있습니다.

 

 

function Person() {

}

Person.prototype = {
	name : "Nick",
    age : 29,
    job : "software Engineer"
};

 

Person에 점을 추가하면 객체를 썼던것과 같은 결과가 나옵니다.

 

하지만 이처럼 쓰면 constructor 객체의 타입을 정확히 알 수 가 없어 constructor값을 직접 지정해 줍니다.

 

 

function Person() {

}

Person.prototype = {
	constructor: Person,
	name : "Nick",
    age : 29,
    job : "software Engineer"
};

var person1 = new Person();

console.log(person1 instanceof Object);
console.log(person1 instanceof Person);
console.log(person1.constructor == Person);
console.log(person1.constructor == Object);

 

 

 

 

네이티브 constructor 프로퍼티는 기본적으로 불가능한 프로퍼티이므로 Object.defineProperty()를 쓰는 편이 좋습니다.

 

 

function Person() {

}

Person.prototype = {
	name : "Nick",
    age : 29,
    job : "software Engineer"
};

Obejct.defineProperty(Person.prototype, "constructor", {
	enumerable: false,
    value: Person
});

 

 

 

프로토타입에서 값을 찾는 작업은 적시 검색이므로 프로퍼티타입이 바뀌면 그 내용은 즉시 인스턴스에도 반영됩니다.

 

function Person() {
    
}

var friend = new Person();

Person.prototype.sayHi = function() {
	console.log("hi");
}

friend.sayHi();

 

 

 

 

protootype 포인터는 생성자가 호출될 때 할당되므로 프로토타입을 다른 객체로 바꾸면 생성자와 원래 프로토 타입 사의 연결이 끊어집니다. 다시 말해 인스턴스는 프로토타입을 가리키는 포인터를 가질 뿐 생성자와 연결 된 것이 아닙니다.

 

function Person() {
}

var friend = new Person();

Person.prototype = {
	name : "Nick",
	sayName : function() {
    	console.log(this.name);
    }
};

friend.sayName();

 

 

 

 

 

프로토타입 패턴은 커스텀 타입을 정의할 때도 유용하지만 네이티브 참조 타입 역시 프로토타입 패턴으로 구현되었으므로 중요합니다.

 

console.log(typeof Array.prototype.sort);
console.log(typeof String.prototype.substring);

 

 

 

 

 

네이티브 객체의 프로토타입을 통해 기본 메서드를 참조할 수 있고 새 메서드를 정의할 수도 있습니다.

 

String.prototype.startsWith = function(text) {
	return this.indexOf(text) == 0;
};

var msg = "Hello world!";
console.log(msg.startsWith("Hello"));

 

 

 

 

프로토타입 패턴에 장점만 있는건 아닙니다.  프로토타입에 존재하는 프로퍼티는 모두 인스턴스에 공유되는데 이런 특징은 함수에는 이상적입니다. 진짜 문제는 프로퍼티가 참조 값을 포함한 경우에 공유가 되어 변화가 반영되는 것입니다.

 

 

function Person() {

}

Person.prototype = {
	constructor: Person,
	name : "Nick",
    age : 29,
    job : "software Engineer",
    friends : [ "Shelly", "Courry" ]
};

var person1 = new Person();
var person2 = new Person();

person1.friends.push("John");

console.log(person1.friends);
console.log(person2.friends);

 

 

 

 

 

일반적으로 인스턴스 프로퍼티는 해당 인스턴스만의 특징으로 쓰기 마련입니다. 이 때문에 프로토 타입을 그대로 쓰는 경우는 드뭅니다.

 

 

커스텁 타입을 정의 할때 가장 널리 쓰이는 방법은 생성자 패턴과 프로토타입 패턴의 조합을 사용합니다.

이런 접근법을 토하면 모든 인스턴스는 자신만의 인스턴스 프로퍼티를 가질 수 있고, 참조 방식을 통해 메서드는 공유하므로 메모리를 절약할 수 있다.

 

 

function Person(name, age, job) {
	this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelly", "Court"];
}

Person.prototype = {
	constructor: Person,
    sayName : function() {
    	console.log(this.name);
    }
};

var person1 = new Person("Nick", 29, "Softeware Engineer");
var person2 = new Person("Greg", 30, "Deginer");

person1.friends.push("Van");

console.log(person1.friends);
console.log(person2.friends);

 

 

 

 

 

이 패턴에서 인스턴스 프로퍼티는 순전히 생성자에서만 정의했고 공유 프로퍼티 constructor와 sayName만 프로토 타입으로 정의하여 메모리를 절약하였다.

 

 

동적 프로토 타입

 

동적 프로토타입 패턴은 모든 정보를 생성자 내부에 캡슐화하여 이런 혼란을 해결하면서 필요한 경우에는 프로토타입을 생성자 내부에서 초기화 하여 생성자와 프로토타입을 모두 쓰는 장점은 취하려는 접근법입니다.

 

 

function Person(name, age, job) {
	this.name = name;
    this.age = age;
    this.job = job;
    
    if(typeof this.sayName != "function") {
    	Person.prototype.sayName = function() {
        	console.log(this.name);
        };
    }
}

var friend = new Person("Nick", 30, "Software Engineer");
friend.sayName();

 

 

 

 

코드에 sayName() 메서드가 존재하지 않는다면 추가하는 역활입니다. 첫번째로 호출된 다음에만 실행되고 다음에는 어떤 변경도 없습니다.

 

 

기생 생성자 패턴

 

 

기생 생성자 패턴은 보통 다른 패턴이 실패할 때 폴백으로 씁니다. 일반적인 생성자처럼 보이지만 사실 다른 객체를 생성하고 반환하는 동작을 래퍼 생성자로 감싸는 겁니다.

 

 

function Person(name, age, job) {
	var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
    	console.log(this.name);
    };
    return o;
}

var friend = new Person("Nick", 29, "Software Engineer");
friend.sayName();

 

 

 

 

Person 생성자가 새 객체를 생성한 다음 프로퍼티와 메서드를 초기화하여 반환합니다. new 연산자를 써서 함수를 생성자로 호출하는 점을 제외하면 팩터리 패턴과 완전히 같습니다.

 

이 패턴을 쓰면 다른 방법으로는 불가능한 객체 생성자를 만들 수 있습니다.

 

function SpecialArray() {
	var values = new Array();
    
    values.push.apply(values, arguments);
    
    values.toPipedString = function() {
    	return this.join("|");
    };
    
    return values;
}

var color = new SpecialArray("red", "blue", "green");
console.log(color.toPipedString());

 

 

 

 

push 로 매개변수 전체를 넘겨서 초기화 하고, 메서드를 인스턴스에 추가하여 배열값을 구분하여 반환합니다.

 

 

방탄 생성자 패턴

 

자바스크립트에는 공용 프로퍼티가 없고 메서드가 this를 참조하지 않는 객체를 가리키는 방탄객체라는 용어가 있습니다.

방탄 객체는 생성된 객체의 인스턴스 메서드가 this를 참조하지 않으며, 생성자를 new 연산자를 통해 호출하는 경우가 없어야 한다.

 

 

function Person(name, age, job) {
	var o = new Object();
    
    o.sayHi = function() {
    	console.log(name + "Hi!~");
    };
    
    return o;
}

var friend = Person("Nick", 29, "software Engineer");
friend.sayHi();

 

 

 

 

 

객체에 메서드나 데이터를 추가할 수는 있지만 객체를 생성할 떄 생성자에 넘겼던 원래 데이터에는 접근할 수 없습니다.