[CookBook] 자바스크립트 객체
1. 자바스크립트 객체 정의하기
함수 생성자와 new 연산자를 활용해 새로운 인스턴스를 생성한다. 또한, 인스턴스 생성 시 함수 내부로 전달되는 this(scope)에 대해 알아본다.
function User(/*string*/uid, /*string*/uname) {
// this 객체 User 생성자(class)의 인스턴스가 아니라면...
if (!(this instanceof User)) return null;
this.uid = uid;
this.uname = uname;
this.constructor = this.constructor;
return this;
}
// this.constructor === User
console.debug(new User('mohwa', 'mohwaName').uid);
console.debug(new User('mohwa', 'mohwaName').uname);
console.debug(new User('mohwa', 'mohwaName').constructor);
try
{
// this.constructor === Window
User('mohwa', 'mohwaName');
console.debug(window['uid']);
console.debug(window['uname']);
}
catch(e){
console.debug('error=' + e.message);
}
1. 자바스크립트에서 함수는 객체이다.
2. Javascript new 단항 연산자의 객체 인스턴스 생성 프로세스
2. 객체 맴버를 비공개로 만들기
"특권 메서드(Closure 활용)"란? 비공개 맴버(private)에 접근 가능하며, 그 자신은 public 인 메서드(getter(), setter()와 동일)
function User(/*string*/uid, /*string*/name) {
// this 객체 User 생성자(class)의 인스턴스가 아니라면...
if (!(this instanceof User)) return null;
this.uid = uid;
var name = name;
// 특권 메서드
this.getName = function () {
return name;
}
this.constructor = this.constructor;
return this;
}
console.debug(new User('mohwa', 'mohwaName').uid);
console.debug(new User('mohwa', 'mohwaName').name);
console.debug(new User('mohwa', 'mohwaName').getName());
3. 프로토타입(객체)으로 객체 확장하기
"프로토타입" 상속을 통해 어떤 객체(내장, 생성된)든 확장 가능하다.
또한, 프로그램 안에서 재사용될 메서드(보통 프로토 타입 맴버에 할당한다. 이유는 생성자 내부의 인스턴스 맴버와 다르게 생성 시 한번만 실행되기 때문이다.)와 같은 경우 효율을 위해 인스턴스 맴버가 아닌 프로토타입 맴버에 추가시킨다.
function User1(/*string*/uid, /*string*/name) {
// this 객체 User 생성자(class)의 인스턴스가 아니라면...
if (!(this instanceof User1)) return null;
// 인스턴스 맴버
this.uid = uid;
this.name = name;
this.constructor = this.constructor;
console.log('Instance members=' + this.uid);
return this;
}
// 프로토타입 맴버(재 사용 메서드 추가)
User1.prototype.getId = function () {
console.log('Prototype members=' + this.uid);
return this.uid;
}
var mohwa1 = new User1('mohwa1', 'mohwaName1');
var mohwa2 = new User1('mohwa2', 'mohwaName2');
var mohwa3 = new User1('mohwa3', 'mohwaName3');
// 한번만 실행된다.
mohwa3.getId();
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
}
console.log(' mohwa '.trim());
자바스크립트 프로토타입 체인
자바스크립트 상속(클래스 방식 상속)
4. 객체 기능 상속하기
자바스크립트 기본 상속
function User3(/*string*/uid, /*string*/name) {
// this 객체 User 생성자(class)의 인스턴스가 아니라면...
if (!(this instanceof User3)) return null;
// 인스턴스 맴버
this.uid = uid;
this.name = name;
return this;
}
// 프로토타입 맴버
User3.prototype.getId = function () {
return this.uid;
}
// 프로토타입 객체에 new User3() 객체연결(가장 기본적인 상속 구조)
function clon1(target) {
target = target || function () { ; };
var f = function () {
return this;
};
var slice = Array.prototype.slice;
// 객체 상속
f.prototype = new target(slice.call(arguments, 1)[0], slice.call(arguments, 1)[1]);
return new f();
}
var clonObject1 = clon1(User3, 'mohwa1', 'mohwaName1');
console.log(clonObject1.uid);
console.log(clonObject1.name);
console.log(clonObject1.getId);
자바스크립트 상속 1
자바스크립트 상속 2
함수 인스턴스 맴버 빌려쓰기(상속)
function User3(/*string*/uid, /*string*/name) {
// this 객체 User 생성자(class)의 인스턴스가 아니라면...
if (!(this instanceof User3) && !(this instanceof clon2)) return null;
// 인스턴스 맴버
this.uid = uid;
this.name = name;
return this;
}
// 함수 인스턴스 맴버 빌려쓰기(상속)
function clon2(target) {
if (!(this instanceof clon2)) return null;
target = target || function () { ; };
// 인스턴스 맴버 복사
target.apply(this, Array.prototype.slice.call(arguments, 1));
return this;
}
var clonObject2 = new clon2(User3, 'mohwa1', 'mohwaName1');
console.log(clonObject2.uid);
console.log(clonObject2.name);
// 인스턴스 맴버만 복사되었기 때문에 프로토타입 맴버인 getId() 메서드는 사용할 수 없다.
console.log(clonObject2.getId);
함수(원형) 복사 및 함수 프로토타입 맴버 복사.
프로토타입 맴버(객체)에 객체를 연결하는 방법이 아닌 다른 방법으로 해당 함수가 가진 모든 맴버(인스턴스, 프로토타입)를 복사하는 방법(하지만 억지스러운 면이 없지 않다.)
function User3(/*string*/uid, /*string*/name) {
// this 객체 User 생성자(class)의 인스턴스가 아니라면...
if (!(this instanceof User3)) return null;
// 인스턴스 맴버
this.uid = uid;
this.name = name;
return this;
}
function clon3(target) {
target = target || function () { ; };
// 함수(User3 원형)복사
var f = target;
// 프로토타입 맴버 복사
f.prototype = target.prototype;
return f;
}
var clonObject3 = new (clon3(User3))('mohwa2', 'mohwaName2');
console.log(clonObject3.uid);
console.log(clonObject3.name);
console.log(clonObject3.getId);
프로토타입 맴버에 User3 객체를 연결했으며, "예제 1번"의 상속 계념과 거의 동일하지만 다른점으로는 "객체" 가 아닌 "함수" 를 반환해 구현 했다는 점이다.
function User3(/*string*/uid, /*string*/name) {
// this 객체 User 생성자(class)의 인스턴스가 아니라면...
if (!(this instanceof User3)) return null;
// 인스턴스 맴버
this.uid = uid;
this.name = name;
return this;
}
function clon4(target) {
target = target || function () { ; };
var f = function () {
return this;
};
var slice = Array.prototype.slice;
// User3 객체를 연결
f.prototype = new target(slice.call(arguments, 1)[0], slice.call(arguments, 1)[1]);
return f;
}
var clonObject4 = new (clon4(User3, 'mohwa3', 'mohwaName3'))();
console.log(clonObject4.uid);
console.log(clonObject4.name);
console.log(clonObject4.getId);
5. 새로운 속성을 정의하여 객체 확장하기
Object.defineProperty 메서드(ECMAScript5)를 활용해 객체 속성을 정의한다.
// 속성 정의
var obj1 = {};
Object.defineProperty && Object.defineProperty(obj1, 'datas', {
value: {
uid: 'mohwa',
name: 'mohwaName'
},
// 속성 변경 유/무
writable: true,
// for in 반복문 검사 유/무
enumerable: true,
// 이미 지정한 속성의 설정 변경 유/무
configurable: false
});
console.log(obj1.datas.uid);
console.log(obj1.datas.name);
var obj2 = {};
// get, set 옵션을 사용 시 value 옵션을 함께 사용할 수 없다.
Object.defineProperty && Object.defineProperty(obj2, 'datas', {
get: function () {
return value;
},
set: function (newValue) {
value = newValue;
}
});
obj2.datas = {};
console.log(obj2.datas.uid);
console.log(obj2.datas.name);
// 여러 개의 속성 한번에 정의
var obj3 = {};
Object.defineProperties && Object.defineProperties(obj3, {
'datas1': {
value: {
uid: 'mohwa1',
name: 'mohwaName1'
},
writable: true
},
'datas2': {
value: {
uid: 'mohwa2',
name: 'mohwaName2'
},
writable: true
}
});
console.log(obj3.datas1.uid);
console.log(obj3.datas2.uid);
ECMAScript5 객체 속성 추가
http://dol2156.tistory.com/archive/20120516
6. 객체 속성 열거하기
var propertys = '';
var obj = {
uid: 'mohwa',
name: 'mohwaName'
}
// Object.keys 속성 사용(ECMAScript5)
propertys = Object.keys && Object.keys(obj).join(',');
console.log(propertys);
// Object.getOwnPropertyNames 속성 사용(ECMAScript5)
propertys = Object.getOwnPropertyNames && Object.getOwnPropertyNames(obj).join(',');
console.log(propertys);
// for in문 검색 결과(기본)
propertys = (function () {
var ownPropety = Object.hasOwnProperty;
var _propertys = [];
for (var n in obj) {
if (ownPropety.call(obj, n)) {
_propertys.push(n);
}
}
return _propertys;
})().join(',');
console.log(propertys);
7. 속성 추가 및 속성 서술자 변경 금지
Object.seal 메서드(ECMAScript5)를 활용해 객체 속성이 추가되는 것은 물론 속성 서술자가 변경되는 것을 막는다.
var obj = {
uid: 'mohwa',
name: 'mohwaName'
}
// 속성 추가 및 서술자 변경 금지
// 객체 속성의 추가, 삭제를 막는다.(수정은 가능)
if (Object.seal) {
Object.seal(obj);
}
try {
// 수정 됨
obj.uid = 'new mohwa';
// 추가 안됨
obj.uid2 = 'new mohwa';
console.log('retuls1=' + obj.uid);
console.log('retuls1=' + obj.uid2);
// 삭제 안됨
delete obj.uid;
console.log('retuls1=' + obj.uid);
}
catch (e) {
console.log(e);
}
var obj = {
uid: 'mohwa',
name: 'mohwaName'
}
// 속성 불변 객체 만들기
// 객체 속성의 모든 기능(추가, 수정, 삭제)를 막는다.
if (Object.freeze) {
Object.freeze(obj);
}
try {
// 수정 안됨
obj.uid = '';
// 추가 안됨
obj.uid2 = '';
console.log('retuls2=' + obj.uid);
console.log('retuls2=' + obj.uid2);
// 삭제 안됨
delete obj.uid;
console.log('retuls2=' + obj.uid);
}
catch (e) {
console.log(e);
}
9. prototype.bind 함수(주어진 함수의 유효범위를 다룬다)
window.name = 'WindowName';
var objObject = {
name: 'objObjectName',
getNewName: function () {
return (function (that) {
console.log('result3=' + that.name);
})(this);
}
};
if (!Function.prototype.bind) {
Function.prototype.bind = function (scope) {
var fn = this;
return fn.apply(scope, arguments);
}
}
// 유효범위(obj11)
objObject.getNewName();
// 유효범위(window)
objObject.getNewName.bind(this)();