객체의 프로퍼티들도 대부분 변경 가능
빈 객체를 정의해놓고 기능 추가 가능
ex) var dog = {}; //빈 객체에서 시작
dog.name - 'Benji'; //프로퍼티 추가
dog.getName = function(){
return dog.name;
} //메서드 추가
객체리터럴 표기법을 쓰면 생성시점에 객체에 기능을 추가할 수 있다
객체를 생성할 때는 직접 만든 생성자 함수를 사용할 수 도 있고,
Object(), Date(), String()등 내장 생성자를 사용할 수도 있다
객체를 생성하는 두가지 방법
var car = {goes:'far'}; //리터럴 사용
var car = new Object();
car.goes = 'far'; //내장 생성자 사용 - 안티패턴
직접 생성자 함수를 만들어 객체를 생성할 수도 있다
var adam = new Person('Adam');
adam.say(); //'I am adam'
아래는 Person생성자 함수를 정의한 예시
var Person = function (name){
this.name = name;
this.say = function (){
return 'I am ' + this.name;
};
};
new와 함께 생성자 함수를 호출하면 함수 안에서 생기는 일
var Person = function (name){
var this = {};//객체리터럴로 새로운 객체 생성
this.name = name;
this.say = function (){
return 'I am' + this.name;
}; //프로퍼티와 메스드를 추가
return this;
}
new Person()을 호출 할때 마다 메모리에 새로운 함수가 생성된다
say()라는 메서드가 인스턴스 별로 달라지는게 아니므로 프로토타입에 추가하는 것이 낫다
Person.prototype.say = function(){
return 'I am' + this.name;
};
재사용되는 멤버는 프로토타입에 추가해야한다
var this = {};
'빈'객체라는게 실제로 텅 빈것이 아니다.
이 객체는 Person의 프로토타입을 상속받는다
var this = Object.create(Person.prototype);
function Waffle(){
this.tastes = 'yummy';
}
var good_morning = Waffle(); //안티패턴
console.log(typeof good_morning); // undefined
console.log(widow.tastes); //yummy
명명규칙
that 사용
function Waffle(){
return{
tastes : 'yummy'
};
}
항상 객체가 반환
var first = new Waffle(),
second = Waffle();
console.log(first.tastes); //'yummy'
console.log(second.tastes); //'yummy'
위 패턴의 문제는 프로토타입과의 연결고리를 잃어버린다
Waffle() 프로토타입에 추가한 멤버를 객체에서 사용할 수 없다
위의 패턴의 문제점을 해결하고 인스턴스객체에서 프로토타입의 프로퍼티들을 사용할 수 있게 하려면
1. 생성자 내부에서 this가 해당 생성자의 인스턴스인지 확인하고
2. new와 함께 스스로를 재호출 하는 것이다
function Waffle(){
if(!(this instanceof Waffle){
return new Waffle();
})
this.tastes = 'yummy';
}
Waffle.prototype.wangtAnother = true;
인스턴스를 판별하는 또다른 범용적인 방법은 생성자 이름을 하드코딩하는 대신 arguments.callee와 비교하는 것이다
if(!(this instanceof arguments.callee)){
return new arguments.callee();
}
이것은 모든 함수가 호출 될 때 내부적으로 arguments라는 객체가 생성되며 이 객체가 함수에 전달된 모든 인자를 담고 있다는 점을 활용한 패턴
------> 더 자세히쓰기
var a = new Array('itsy', 'bitsy', 'spider'); //안티패턴
var a = ['itsy', 'bitsy', 'spider'];
console.log(typeof a); //배열도 객체이기 때문에 object가 출력
console.log(a.constructor === Array); //true
Array() 생성자에 숫자 하나를 전달할 경우, 이 값은 배열의 첫번째 원소값이 되는게 아니라 배열의 길이를 지정한다
즉, Array(3)은 길이가 3이고 실제 원소값은 가지지않는 배열을 생성한다
아래는 리터럴과 생성자 함수의 서로 다른 동작 방식을 보여준다
//한 개의 원소를 가지는 배열
var a = [3];
console.log(a.lengh);//1
console.log(a[0]);//3
//세 개의 원소를 가지는 배열
var a = new Array(3);
console.log(a.length); //3
console.log(typeof a[0]); //undefined
new Array()에 정수가 아닌 부동소수점을 가지는 수를 전달 할 경우
부동소수점은 배열의 길이로 유효한 값이 아니기 때문에 에러가 발생한다
런타임에 동적으로 배열을 생성할 경우 에러 발생을 피하려면 배열의 리터럴 표기법을 쓰는것이 안정적이다
배열인지 판단하는 방법
배열에 typeof연산자를 사용하면 'object'가 반환된다
console.log(typeof[1,2]); //object
값이 실제로 배열인지 알아내야 하는 경우가 자주 있다
하지만 배열이 아닌 객체가 똑같은 이름의 프로퍼티나 메서드를 가지지 말란 법은 없다
ECMAScript5에서는 Array.isArray()라는 새로운 매서드가 정의되어있어 이 메서드는 인자가 배열이면 true를 반환한다
Array.isArray([]); //true
//배열과 비슷한 객체로 속여본다
Array.isArray({
length:1,
'0':1,
slice: function(){}
}); //false가 반환된다
이 매서드를 사용할 수 없는 경우에는 Object.prototype.toString()메서드를 호출하여 판별
배열 판단 매서드
if(typeof Array.isArray === 'undefined'){
Array.isArray = function (arg){
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
eval보다 JSON.parse()를 사용하는 것이 최선책이다
//입력되는 JSON 문자열
var jstr = '{'mykey':'my value'}';
//안티패턴
var data = eval('('+jstr+')');
//권장안
var data = JSON.parse(jstr);
console.log(data.mykey);
JSON.parse()의 반대는 JSON.stringify()이다
이 메서드는 객체 또는 배열을 인자로 받아 JSON문자열로 직렬화한다
var dog = {
name :'Fido',
dob : new Date(),
legs : [1,2,3,4]
};
var jsonstr = JSON.stringify(dog);
정규표현식 역시 객체이며 정규식을 생성하는 방법은 2가지이다
//정규식 리터럴
var re = /\\/gm;
//생성자
var re = new RegExp('\\\\', 'gm');
매칭에 사용되는 정규식 패턴을 슬래시로 감싼다
두번째 슬러시 뒤에는 따옴표없이 문자 형태의 변경자를 둘 수 있다
g:전역매칭
m:여러 줄 매칭
i:대소문자 구분 없이 매칭
자바스크립트에는 숫자, 문자열, 불린, null, undefined의 다섯가지 원시데이터타입이 있다
null, undefined를 제외한 나머지 세개는 원시 데이터 타입 래퍼라 불리는 객체를 가지고 있다
이 래퍼 객체는 각각 내장 생성자인 Number(), String(), Boolean()을 사용하여 생성된다
아래는 원시데이터 타입 숫자와 숫자 객체의 차이를 보여준다
//원시 데이터 타입 숫자
var n = 100;
console.log(typeof n); //'numer'
//숫자 객체
var nobj = new Number(100);
console.log(typeof nobj); //'object'
숫자객체에는 toFixed()와 toExponential()같은 메서드가 있고
문자열객체에는 substring(), charAt(), toLowerCase()같은 메서드와 length 프로퍼티가 있다
메서드를 호출하는 순간 내부적으로는 원시 데이터 타입 값이 객체로 임시변환되어 객체처럼 동작한다
//원시 데이터 타입 문자열을 객체로 사용한다
var s = 'hello';
console.log(s.toUpeprCase()); //'HELLO'
//값자체만으로도 객체처럼 동작할 수 있다
'monkey'.slice(3,6);//'key'
//숫자도 마찬가지다
(22/7).toPrecision(3); //'3.14'
값을 확장하거나 상태를 지속시키기 위해 래퍼 객체를 쓰는 경우도 있다
원시데이터타입은 객체가 아니기 때문에 프로퍼티를 추가하여 확장할 수가 없다
//원시 데이터 타입 문자열
var greet = 'Hello there';
//split() 메서드를 쓰기 위해 원시 데이터타입이 객체로 변환된다
greet.split(' ')[0]; //'Hello'
//원시데이터 타입에 확장을 시도할 경우 에러는 발생하지 않는다
greet.smile = true;
//그러나 실제로 동작하는것은 아니다
typeof greet.smile;//'undefined'
위에서 greet프로퍼티 또는 메서드에 접근하는 작업을 에러 없이 처리하기 위해 일시적으로만 객체로 변환된것 뿐이다
만약 greet가 newString()을 사용하여 객체로 정의되었다면 기대한대로 smile프로퍼티가 생성되었을 것이다
new를 빼먹고 래퍼 생성자를 사용하면 래퍼생성자가 인자를 원시데이터 타입의 값으로 변환된다
typeof Number(1);//'number'
typeof Number('1');//'number'
typeof Number(new Number());//'number'
typeof string(1);//'string'
typeof Boolean(1);//'boolean'
js에는 Error(), SyntaxError(), TypeError()등 여러 가지 에러 생성자가 내장되어 있으며 throw문과 함께 사용된다
throw문은 어떤 객체와도 함께 사용할 수 있다
반드시 에러 생성자를 통해 객체를 생성해야 하는 것은 아니며 직접 정의한 객체를 던질 수도 있다
이 객체는 'name'과 'message'외에도 임의의 프로퍼티를 가질 수 있기 때문에 catch문에서 처리할 정보를 담아 전달하면 된다
try{
//에러를 발생시킨다
throw{
name:'MyErrorType',// 임의의 에러타입
message:'oops',
extra:'This was rather embarrassing',
remedy:genericErorHandler//에러를 처리할 함수
};
}catch(e){
//사용자에게 공지한다
alert(e.message);//'oops'
//훌륭하게 예외를 처리한다
e.remedy();//genericErorHandler()호출
}
에러 생성자를 new없이 일반 함수로 호출해도 new를 써서 생성자로 호출한 것과 동일하게 동작하여 여러 객체가 반환된다
객체리터럴표기법 : 이름-값 쌍으로 쉼표로 분리하고 괄호로 감싸 객체를 만듬
생성자 함수 : 내장생성자의 경우 객체리터럴을 사용하는 것이 낫다
배열 리터럴 표기법 : 대괄호 안에서 값 목록을 쉼표로 분리한다
JSON : 객체와 배열 표기법으로 이루어진 데이터 형식