객체는 이름과 값을 한 쌍으로 묶은 데이터를 여러 개 모은 것이다.
이름 값 suit "하트" rank "A" 객체에 포함된 데이터 하나(이름과 값의 쌍) 를 가리켜 객체의
프로퍼티
라고 부른다. 프로퍼티의 이름 부분 (suit, rank)는프로퍼티 이름
또는키
라고 부른다.
// 객체 생성
var card = { suit: "하트", rank: "A" };
// 객체 안의 프로퍼티 값을 읽거나 사용 할 때
card.suit; // -> 하트
card["rank"]; // -> A
// 객체에 없는 프로퍼티를 읽을 때
card.color; // -> undefined
// 빈 객체 생성
var obj = {};
console.log(obj); // -> Object{}
프로퍼티 이름과 값은 콜론 :
으로 구분하며 중괄호({}) 안에 있는 프로퍼티들은 쉼표 ,
로 구분한다. 객체 안의 프로퍼티 값을 읽거나 쓸 때는 마침표 .
또는 대괄호 []
를 사용한다.
객체에 없는 프로퍼티를 읽으려고 시도하면 undefined
를 반환하고 객체 리터럴 안에 프로퍼티를 작성하지 않으면 빈 객체가 생성된다.
// 새로운 프로퍼티 추가
card.value = 14;
console.log(card); // -> Object {suit : "하트", rank : "A", value : 14}
// delete 연산자로 프로퍼티 삭제
delete card.rank;
console.log(card); // -> Object {suit : "하트", value : 14}
위의 코드처럼 자바스크립트의 객체는 실행 중에 프로퍼티를 자유롭게 추가하거나 삭제 가능하다.
in
연산자로 특정 프로퍼티가 객체 안에 있는지 확인가능하다. 객체 안에 포함되어 있다면 true
를 반환, 포함되지 않았다면 false
를 반환한다.
var card = { suit: "하트", rank: "A" };
console.log("suit" in card); // -> true
console.log("color" in card); // -> false
var p = { x: 1.0, y: 2.5 };
var circle = {
center: { x: 1.0, y: 2.0 },
radius: 2.5,
};
circle.center.x; // -> 1.0
객체의 프로퍼티 값으로 객체를 대입할 수도 있다. (마침표 뒤에 프로퍼티 이름을 연결)
객체 타입의 값을 변수에 대입하면 그 변수에는 객체의 참조 (메모리에서의 위치 정보)
가 저장된다. 이때 변수 상태를 가리켜 그 객체를 참조하고 있다
라고 한다. 다음 코드를 보자
// 위에서 만든 card 객체를 a 에 대입
var a = card;
// a 가 card 객체를 참조하게 되므로 읽거나 수정할 수 있다.
console.log(a.suit); // -> 하트
a.suit = "스페이드";
console.log(a.suit); // -> 스페이드
console.log(card.suit); // -> 스페이드
8장에 자세히 나와있지만 기초를 다지기 위해 훑어보자
일련의 처리를 하나로 모아 언제든 호출할 수 있도록 만들어 둔 것이다. 함수의 입력 값을 인수
라고 부르고 함수의 출력 값을 반환값
이라고 부른다.
function square(x) {
return x * x;
}
return
문이 실행되면 제어권이 함수를 호출한 코드로 되돌아가고, return
문에 지정된 값은 함수의 반환값이 된다.
함수 이름만 잘 붙혀도 그 함수가 어떤 일을 하는지 알 수 있고 가독성과 유지 보수성이 크게 높아진다. 함수 이름은 일반적으로 동사 또는 동사로 시작되는 어휘로 시작한다. 캐멀 표기법
또는 밑줄 표기법
을 사용한다.
function saveImage(img) {...}
function load_file() {...}
함수 이름 뒤에 소괄호로 인수를 묶어 입력한다.
square(3);
함수를 호출할 때 전달하는 값(3)을 인수 (argument)
, 함수 정의문의 인수를 인자 (parameter)
라고 한다.
함수는 인수를 여러 개를 쉼표 ,
로 구분하여 받을 수 있다.
// 인수 여러 개 받기
function dist(p,q) {
var dx = q.x - p.x;
var dy = q.y - p.y;
}
var p1 = {x:1, y:1},
var p2 = {x:4, y:5},
var d = dist(p1,p2);
// 인수가 없을 때
var bark = function() {console.log("Wow");};
bark(); // -> "Wow"
자바스크립트의 함수 선언문은 프로그램의 어떤 위치에도 작성할 수 있다. 함수를 실행한 후 함수를 정의해도 문제 없이 동작한다.
console.log(square(5)); // -> 25
function square(x) {
return x * x;
}
자바스크립트에서는 함수가 객체이다. 즉 변수에 함수를 할당하고 그 변수 이름으로 함수를 실행할 수 있다.
var sq = square;
console.log(square(5)); // -> 25
function add1(x) {
return (x = x + 1);
}
var a = 3;
var b = add1(a);
console.log("a = " + a + ", b = " + b); // -> a = 3, b = 4
// 인수가 객체일 때
function add1(p) {
p.x = p.x + 1;
p.y = p.y + 1;
return p;
}
var a = { x: 3, y: 4 };
var b = add1(a);
console.log(a, b); // -> Object {x = 4, y = 5} Object {x = 4, y = 5}
add1은 전달받은 인수에 1을 더하여 반환하는 함수이다. 이 함수가 호출될 떄 변수 a의 복사본이 인자 x에 할당된다. 즉, 인수에 원시 값을 넘기면 그 값 자체가 인자에 전달된다. 이를 가리켜 값의 전달
이라고 부른다. 이때 변수 a 와 x 는 다른 영역의 메모리에 위치한 별개의 변수이기 때문에 x 값을 바꾸더라도 a 값은 바뀌지 않는다.
인수 여러 개를 우아하게 전달하는 방법
함수에 넘겨야 하는 인수 개수가 많아지면 다음과 같은 문제가 발생한다.
객체의 프로퍼티에 인수를 담아서 넘기면 이러한 문제를 해결 할 수 있다.
// 인자가 많은 함수
function setBallProperties(x,y,vx,vy,radius) {...}
...
setBallProperties(0,0,10,15,5);
// 객체의 프로퍼티에 인수를 담아서 해결
var parameters = {
x : 0,
y : 0,
vx : 10,
vy : 15,
radius : 5
}
function setBallProperties(parameters) {...}
...
setBallProperties(parameters);
전역 유효 범위와 지역 유효 범위
변수에 접근할 수 있는 범위를 그 변수의 유효 범위
라고 한다. 자바스크립트 변수는 변수의 유효 범위에 따라 전역 변수
와 지역 변수
로 나뉜다.
전역 변수
는 함수 바깥에서 선언된 변수이고 유효 범위가 전체 프로그램이다. 반대로 지역 변수
는 함수 안에서 선언된 변수이고 유효 범위는 함수 내부이다.
변수의 충돌
변수에 유효 범위가 있는 이유는 프로그램의 다른 부분에서 이름이 같은 변수끼리 충돌하지 않도록 하기 위해서다. 다른 함수 내부에서 각각의 지역 변수로 선언된 변수는 함수 내에서만 유효하므로 충돌하지 않는다. 하지만 전역 변수는 이름이 같으면 충돌하기 때문에 주의해야 한다.
함수 안에서의 변수 선언과 변수 끌어올림
함수 중간 부분에서 변수를 선언하더라도 변수는 함수 첫 머리에서 선언된 것처럼 함수 안의 다른 문장보다 먼저 선언된다
function f() {
console.log(a); // -> undefined
var a = "local";
console.log(a); // -> local
return a;
}
함수 안에서의 변수 선언 생략
변수를 선언하지 않은 상태에서 값을 대입하면 전역 변수로 선언된다.
function f() {
a = "local";
console.log(a); // -> local
return a;
}
console.log(a); // -> local
let
과 const
는 ECMAScript6 부터 추가된 변수 선언자로 모두가 '블록 유효 범위'를 갖는 변수를 선언한다.
let
// 변수 선언
let x;
// 변수 동시 선언
let a, b, c;
// 선언과 동시에 초깃값 설정
let x = 5,
y = 7;
var
와 let
의 가장 큰 차이점은 let으로 선언한 변수의 유효 범위가 블록 안이라는 점이다. 이러한 차이점 때문에 var
보다 let
을 많이 쓴다.
let x = "outer x";
{
let x = "inner x";
let y = "inner y";
console.log(x); // -> inner x
console.log(y); // -> inner y
}
console.log(x); // -> outer x
console.log(y); // -> ReferenceError : y is not defined
const
const
문은 블록 유효 범위를 가지면서 한번만 할당할 수 있는 변수(상수)를 선언한다. let
문으로 선언한 변수처럼 동작하지만 반드시 초기값을 설정해야 한다는 차이점이 있다.
const x = 2;
x = 5; // -> Uncaught TypeError (const문은 값을 바꿀 수 없는 상수이다)
const origin = { x: 1, y: 2 };
origin.x = 3;
console.log(origin); // -> Object {x : 3, y : 2} (상수 값이 객체나 배열이면 값을 바꿀 수 있다.)
console.log(square(3)); // -> 호이스팅 X TypeError 등장
var square = function (x) {
return x * x;
};
// 익명함수에도 이름을 붙일 수 있다.
var square = function sq(x) {
return x * x;
};
위 코드에서는 function(x) {...} 부분이 함수 리터럴이다. 함수 리터럴
은 이름이 없는 함수이므로 익명 함수
또는 무명 함수
라고 부른다. 함수 리터럴을 사용할 때는 끝에 반드시 세미콜론을 붙여야 한다. 함수 선언문으로 정의한 함수는 호이스팅이 적용되지만 함수 리터럴로 정의한 함수는 호이스팅이 적용되지 않는다.
객체의 프로퍼티 중에서 함수 객체의 참조를 값으로 담고 있는 프로퍼티를 가리켜 메서드
라고 부른다.
var circle = {
center: { x: 1.0, y: 2.0 }, // 원의 중점을 표현하는 객체
radius: 2.5, // 원의 반지름
area: function () {
// 원의 넓이를 구하는 메서드
return Math.PI * this.radius * this.radius;
},
};
// 원을 평행으로 이동시키는 translate 메서드 추가
circle.translate = function (a, b) {
this.center.x = this.center.x + a;
this.center.y = this.center.y + b;
};
// 메서드 실행
circle.translate(1, 2);
circle.center; //h -> Object {x=2, y=4}
위 코드에 return에 나오는 this
는 그 함수를 메서드로 가지고 있는 객체 (위 코드에선 circle)를 가리킨다. 즉, this.radius
가 circle.radius
이다. 메서드 또한 프로퍼티의 일종이므로 나중에 추가할 수 있다.
- 재사용할 수 있다.
- 만든 프로그램을 이해하기 쉽다.
- 프로그램 수정이 간단해진다.
자바스크립트에선 C++과 Java와 달리 class
가 없다. 대신 생성자라고 하는 함수로 객체를 생성할 수 있다.
function Card(suit, rank) {
this.suit = suit;
this.rank = rank;
}
// 생성자로 객체를 생성할 때는 "new" 연산자를 사용
var card = new Card("하트", "A");
console.log(card); // -> Card {suit: "하트", rank: "A"}
위의 코드를 실행하면 suit 프로퍼티에는 "하트", rank 프로퍼티에는 "A"라는 값이 저장된 객체가 생성되고, 마지막으로 그 객체의 참조가 변수 card에 할당된다.
생성자
위의 예처럼 new 연산자로 객체를 생성할 것이라 기대하고 만든 함수를 생성자
라고 한다. 생성자의 이름은 첫글자를 대문자로 쓰는 파스칼 표기법
을 사용한다. 그리고 생성자와 new 연산자로 생성한 객체를 그 생성자의 인스턴스
라고 부른다.
생성자의 역할
생성자는 객체를 생성하고 초기화하는 역할을 한다. 생성자를 사용하면 이름은 같지만 프로퍼티 값이 다른 객체(인스턴스) 여러 개를 간단히 생성 할 수 있다.
var card1 = new Card("하트", "A");
var card2 = new Card("클럽", "K");
var card3 = new Card("스페이드", "2");
생성자에서 this.프로퍼티 이름
에 함수의 참조를 대입하면 메서드를 정의할 수 있다.
function Circle(center, radius) {
this.center = center;
this.radius = radius;
this.area = function () {
return Math.PI * this.radius * this.radius;
};
}
var p = { x: 0, y: 0 };
var c = new Circle(p, 2.0);
console.log("넓이 = " + c.area()); // -> 넓이 12.566370614359172
사용자가 정의하는 생성자 외에도 자바스크립트에 내장되어있는 내장 생성자
가 있다. 자주 쓰이는 내장 생성자를 알아보자
생성자 이름 | 생성되는 객체 |
---|---|
String | 문자열 객체 |
Number | 숫자 객체 |
Array | 배열 |
Date | 날짜와 시간 |
Function | 함수 객체 |
Map | key/value맵을 생성 |
... | ... |
객체도 마찬가지로 자바스크립트에 내장되어있는 내장 객체
가 있다.
내장 객체 | 설명 |
---|---|
전역 객체 | 프로그램 어디에서나 사용할 수 있는 객체 |
Json | JSON을 처리하는 기능 제공 |
Math | 수학적인 함수와 상수 제공 |
Reflect | 프로그램 흐름을 가로채는 기능 |
위의 표에서 소개한 전역 객체는 자바스크립트에서 매우 중요한 객체이다. 전역 객체의 프로퍼티는 프로그램의 어느 위치에서나 사용할 수 있다.
분류 | 프로퍼티 |
---|---|
전역 프로퍼티 | undefined, NaN, Infinity |
생성자 | Object(), String(), Number() 등 |
전역 함수 | parseInt(), parseFloat(), isNaN() 등 |
내장 객체 | Math, JSON, Reflect |
배열 리터럴은 쉼표로 구분한 값을 대괄호[]
로 묶어서 표현한다.
var evens = [2, 4, 6, 8];
// 배열 리터럴 안에 어떠한 요소도 작성하지 않으면 빈 배열이 생성
var empty = [];
console.log(empty); // -> []
// 배열 리터럴 요소의 값을 생략하면 생성되지 않는다
var a = [2, , 4];
console.log(a); // -> [2, undefined, 4]
// 변수와 마찬가지로 모든 타입의 값이 올 수 있다
var various = [3.14, "pi", true, { x: 1, y: 2 }, [2, 4, 6, 8]];
배열 요소의 최대 인덱스 값 +1이 담겨 있다. 즉, 배열 길이
를 구할 수 있다.
var evens = [2, 4, 6, 8];
evens.length; // -> 4
// 배열의 길이를 줄일 수 있다.
var a = [1, 2, 3, 4, 5];
a.length = 2;
console.log(a); // -> [1,2];
배열을 Array 생성자로 생성할 수 있다.
var evens = new Array(2, 4, 6, 8);
var empty = new Array();
var a = new Array(2, 4);
var v = new Array(3.14, "pi", true);
// Array 생성자의 인수가 한개고 그 값이 양의 정수면 길이를 뜻한다.
var x = new Array(3);
console.log(x.length); // -> 3;
// 음수이면 오류 발생
var x = new Array(-3); // -> 에러
Array 객체는 배열의 인덱스를 문자열로 변환해서 그것을 프로퍼티로 이용한다. 즉, 배열에 대괄호 연산자를 사용하는 것은 객체에 대괄호 연산자를 사용하는 것과 마찬가지이고, 배여르이 요소 번호로 숫자 값 대신 문자열을 사용할 수 있다.
var a = ["A", "B", "C", "D"];
console.log(a["2"]); // -> C
// 없는 배열 요소를 읽으면 undefined 반환
a[4]; // undefined
없는 배열 요소에 값을 대입하면 새로운 요소가 추가된다.
var a = ["A", "B", "C"];
a[3] = "D";
console.log(a); // -> ["A", "B", "C", "D"]
// push() 메서드러 추가
var a = ["A", "B", "C"];
a.push("D");
console.log(a); // -> ["A", "B", "C", "D"]
// delete() 메서드로 삭제
delete a[1];
console.log(a); // -> ["A", undefined, "C", "D"]