JavaScript의 본질
JavaScript는 다음과 같은 특징을 가진 프로그래밍 언어입니다:
- 객체 기반
- JavaScript는 다양한 내장 객체를 제공합니다【String, Date, Array 등】
- 개발자가 직접 커스텀 객체를 생성할 수도 있습니다
- 이벤트驱动
- 사용자가 특정 동작을 수행할 때【클릭, 마우스 이동】, JavaScript는 이러한 이벤트를 감지하는 메커니즘을 제공합니다. 이벤트가 발생하면 미리 정의된 코드가 실행됩니다
- 인터프리터 언어
- JavaScript 코드는 브라우저에 의해 직접 해석되며, 컴파일 과정이 필요하지 않습니다
- 브라우저 기반 동적 인터랙션 기술
- JavaScript가 브라우저에서 실행되므로, 웹 페이지에 동적인 기능을 추가할 수 있습니다
- 동적 타이핑
- Java나 C++ 같은 정적 타이핑 언어는 변수를 먼저 선언한 후 사용해야 하지만, JavaScript는 선언 없이 바로 사용할 수 있습니다
JavaScript 변수의 타입
JavaScript 변수는 세 가지 주요 타입으로 구분됩니다:
- 원시 타입【number, string, boolean】
- JavaScript는 동적 타이핑 언어로, 실행 시점에 타입이 결정됩니다. 모든 변수에 var로 선언합니다
- 특수 타입【null, undefined】
- 변수를 선언만 하고 값을 할당하지 않으면 undefined 타입이 됩니다
- 참조 타입【배열, 객체, 함수】
JavaScript 객체의 분류
JavaScript에서 객체는 크게 4가지로 분류됩니다:
- 내장 객체【String, Math, Array】
- 커스텀 객체【개발자가 직접 생성한 객체】
- 브라우저 객체【window, document, history 등 브라우저와 관련된 객체】
- ActiveXObject(XMLHttpRequest) 객체【비동기 통신을 위한 객체로, AJAX에서 서버와 데이터를 주고받을 때 사용】
함수 정의的三种方式
함수는 참조 타입에 속하며, JavaScript에서 함수를 정의하는 방법은 여러 가지입니다.
중요한 점은: JavaScript에서 함수를 정의할 때 매개변수의 타입을 선언하지 않습니다!
일반적 방식
아래는 calcSum이라는 이름의 함수를 정의한 예입니다
function calcSum(num1, num2) {
return num1 + num2;
}
var result = calcSum(10, 20);
console.log("결과: " + result);
Function 생성자 사용
JavaScript에서 모든 것이 객체이므로, 함수도 Function 객체로 표현할 수 있습니다:
var yourResult = new Function("num1", "num2", "return num1 + num2");
console.log(yourResult(100, 200));
이 방식은 가독성이 떨어지고 잘 사용되지 않습니다【비추천】
익명 함수 할당
첫 번째 방식과 유사하지만, 이름 없는 함수를 변수에 할당하는 방식입니다:
var theyResult = function(num1, num2) {
return num1 + num2;
};
console.log(theyResult(50, 30));
theyResult 변수가 해당 함수를 참조하게 됩니다
객체 생성
방식 ①
new Object() 사용
var myObject = new Object();
방식 ②
중괄호 {} 사용
var myObject2 = {};
속성 추가 및 접근
생성한 객체에 속성을 추가하고 값을 접근하는 방법입니다!
점 연산자로 속성 추가
JavaScript는 동적 타이핑 언이므로 속성을 동적으로 추가할 수 있습니다:
myObject.age = 25;
myObject.name = "김철수";
myObject.greet = function() {
console.log("안녕하세요");
};
점 연산자로 속성 접근
var ageValue = myObject.age;
var nameValue = myObject.name;
대괄호 연산자로 속성 접근
var ageValue = myObject["age"];
var nameValue = myObject["name"];
클래스 생성
방식 ①
function을 사용하여 클래스를 흉내내기, function이 생성자 역할을 합니다
function test() {
var instructor = new Instructor();
}
function Instructor() {
// 생성자 구현
}
방식 ②
위의 방식은 함수와 혼동될 수 있습니다.
보통 다음과 같이 합니다: 익명 함수를 변수에 할당하여 클래스처럼 사용
function test() {
var instructor = new Instructor();
}
var Instructor = function() {
// 생성자 구현
};
방식 ③
JSON 문법을 사용하여 클래스를 생성, 객체 리터럴 방식으로 메소드 정의
var userProfile = {
age: 30,
username: "이영희",
sayHello: function() {
console.log("환영합니다");
}
};
공개 속성 및 메소드
공개 속성은 클래스 내에서 this로 정의하고, 공개 메소드는 prototype을 사용합니다
prototype으로 정의된 속성은 Java의 정적 멤버와 유사합니다: 프로토타입 객체에 정의된 속성은 해당 생성자로 생성된 모든 객체가 공유합니다
var Person = function Person(name) {
this.name = name;
if (typeof Person._initialized == "undefined") {
Person.prototype.setName = function(newName) {
this.name = newName;
};
Person.prototype.getName = function() {
console.log(this.name);
};
}
Person._initialized = true;
};
두 개의 서로 다른 Person 객체를 생성하면, name 속성은 개별적이지만 setName()과 getName() 메소드는 공유됩니다
중요한 점: prototype으로 정의된 속성은 읽기 전용입니다. 구체적인 객체에 prototype 속성을 쓰려고 하면, 동일한 이름의 속성을 새로 정의하는 것입니다. 같은 이름의 속성을 읽을 때 우선적으로 객체 자신의 속성을 읽습니다
비공개 속성
Java에서는 private 키워드로 비공개 속성을 정의합니다.
JavaScript에는 이러한 키워드가 없으므로, 다음과 같이 구현합니다: 함수 내부【생성자】에 정의된 변수가 비공개 변수가 됩니다
var Student = function Student(name) {
// 비공개 속성, 외부에서 접근 불가
var age = 20;
// 공개 속성, 외부에서 접근 가능
this.name = name;
// age에 접근하려면 여기서 메소드를 정의해야 합니다
// 다른 곳에서는 접근할 수 없습니다!
// 보통 공개 메소드를 통해 비공개 속성에 접근하도록 구현합니다
this.getAge = function() {
return age;
};
};
정적 속성 및 메소드
JavaScript에서 정적 속성은 prototype이 아닌 생성자 함수 자체에 직접 정의합니다
정적을 정의하는 시점:
- 클래스의某个 값이 객체와 무관하고 모든 위치에서 동일한 결과를期望할 때 정적 속성으로 정의
- 클래스의 메소드가 특정 객체와 무관한 작업【공통 유틸리티 기능 등】을 수행할 때는 정적 메소드로 정의
// 정적 속성 CATEGORY
Product.CATEGORY = "전자기기";
Product.displayCategory = function() {
console.log(Product.CATEGORY);
};
JavaScript의 for-in 루프
for-in 루프는 본질적으로 forEach와 유사하며, 두 가지 주요 용도가 있습니다
- 배열 순회
- JavaScript 객체 순회
배열 순회
for-in으로 배열을 순회할 때, 루프 카운터는 배열 요소의 인덱스입니다
var fruits = ['사과', '바나나', '오렌지'];
for (var idx in fruits) {
console.log(idx);
}
JavaScript 객체 순회
for-in으로 객체를 순회할 때, 루프 카운터는 객체의 속성 이름입니다
var data = {x: {value: 10}, y: {value: 20}};
for (var prop in data) {
console.log(prop);
}
JavaScript 풍선 터뜨리기 게임
HTML5와 CSS3를 활용하면 재미있는 게임을 만들 수 있습니다. 다음은 웹 브라우저에서 실행되는 풍선 터뜨리기 게임의 구현 코드입니다. 풍선이 화면 아래에서 위로 올라오며, 클릭하면 터지는 간단한 게임입니다
<!DOCTYPE html>
<html>
<head>
<title>풍선 터뜨리기</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>
body { margin: 0; padding: 0; overflow: hidden; }
#gameArea { position: fixed; top: 0; left: 0; width: 100%; height: 100%; }
.balloon {
width: 120px;
height: 160px;
position: absolute;
background: linear-gradient(135deg, #ff6b6b, #ee5a5a);
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
box-shadow: inset -10px -10px 30px rgba(0,0,0,0.1), 5px 5px 15px rgba(0,0,0,0.2);
z-index: 5;
}
.balloon::after {
content: "";
width: 15px;
height: 15px;
background: transparent;
position: absolute;
right: -8px;
bottom: -8px;
border-right: 3px solid rgba(0,0,0,0.3);
border-bottom: 3px solid rgba(0,0,0,0.3);
transform: rotate(-45deg);
}
.balloon::before {
content: "";
width: 2px;
height: 40px;
background: rgba(0,0,0,0.4);
position: absolute;
left: 50%;
bottom: -35px;
transform: translateX(-50%);
}
#scorePanel {
position: fixed;
top: 20px;
left: 20px;
font-family: sans-serif;
font-size: 18px;
color: #333;
z-index: 100;
background: rgba(255,255,255,0.8);
padding: 15px;
border-radius: 10px;
}
#gameOver {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 30px;
border-radius: 15px;
text-align: center;
z-index: 200;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
}
#gameOver h2 { color: #e74c3c; margin-top: 0; }
button {
background: #3498db;
color: white;
border: none;
padding: 12px 25px;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
margin-top: 15px;
}
button:hover { background: #2980b9; }
</style>
</head>
<body>
<div id="scorePanel">
<div>최고 점수: <span id="highScore">0</span></div>
<div>현재 점수: <span id="currentScore">0</span></div>
</div>
<div id="gameOver">
<h2>게임 오버!</h2>
<p>최종 점수: <span id="finalScore">0</span></p>
<button onclick="location.reload()">다시 시작</button>
</div>
<div id="gameArea"></div>
<script>
(function() {
var highScore = parseInt(localStorage.getItem('balloonHighScore')) || 0;
var currentScore = 0;
var balloonPool = [];
var random = Math.random;
var screenW = window.innerWidth;
var screenH = window.innerHeight;
var balloonWidth = 130;
var balloonHeight = 170;
var minSpeed = 2;
var speedRange = 4;
var initialBalloons = 6;
var moveInterval;
var isGameOver = false;
var balloonId = 1;
var gameArea = document.getElementById('gameArea');
document.getElementById('highScore').textContent = highScore;
function updateScore() {
document.getElementById('currentScore').textContent = currentScore;
if (currentScore > highScore) {
highScore = currentScore;
document.getElementById('highScore').textContent = highScore;
localStorage.setItem('balloonHighScore', highScore);
}
}
function gameOver() {
isGameOver = true;
clearTimeout(moveInterval);
balloonPool = [];
document.getElementById('finalScore').textContent = currentScore;
document.getElementById('gameOver').style.display = 'block';
}
function createBalloons(count) {
var fragment = document.createDocumentFragment();
for (var i = 0; i < count; i++) {
var balloon = document.createElement('div');
balloon.className = 'balloon';
var speed = Math.max(minSpeed, ~~(random() * speedRange));
balloon.setAttribute('data-speed', speed);
balloon.setAttribute('id', 'balloon-' + balloonId++);
var xPos = (~~(random() * screenW)) - balloonWidth;
xPos = Math.max(0, Math.min(xPos, screenW - balloonWidth));
balloon.style.left = xPos + 'px';
balloon.style.top = screenH + 'px';
var hue = ~~(random() * 360);
balloon.style.background = 'linear-gradient(135deg, hsl(' + hue + ', 70%, 60%), hsl(' + hue + ', 80%, 50%))';
fragment.appendChild(balloon);
balloonPool.push(balloon);
}
gameArea.appendChild(fragment);
}
function animateBalloons() {
var len = balloonPool.length;
for (var i = 0; i < len; i++) {
var currentBalloon = balloonPool[i];
if (!currentBalloon) continue;
var topPos = currentBalloon.offsetTop;
if (topPos > -180) {
var speed = parseInt(currentBalloon.getAttribute('data-speed'));
currentBalloon.style.top = (topPos - speed) + 'px';
} else {
gameOver();
return;
}
}
moveInterval = setTimeout(animateBalloons, 1000 / 30);
}
function handleClick(event) {
if (isGameOver) return;
if (event.target.className === 'balloon') {
var index = balloonPool.lastIndexOf(event.target);
if (index > -1) {
balloonPool.splice(index, 1);
burst.call(event.target);
}
}
}
function burst() {
var speed = parseInt(this.getAttribute('data-speed'));
var opacity = 1;
var fadeTimer = setInterval(function() {
opacity -= 0.05;
this.style.opacity = opacity;
this.style.transform = 'scale(' + opacity + ') rotate(' + (45 + (1-opacity)*180) + 'deg)';
if (opacity <= 0) {
clearInterval(fadeTimer);
if (this.parentNode) {
this.parentNode.removeChild(this);
}
currentScore++;
updateScore();
if (!isGameOver) {
createBalloons(1);
}
}
}.bind(this), 30);
}
gameArea.addEventListener('click', handleClick, false);
createBalloons(initialBalloons);
animateBalloons();
})();
</script>
</body>
</html>
이 프로젝트를 통해 배운 중요한 기술 포인트:
- CSS3를 활용하면 기본적인 div 요소를 다양한 형태로 변형할 수 있습니다【타원형, 그림자 효과 등】
- 가상 요소를 사용하면 HTML 태그를 추가하지 않고도 복잡한 디자인을 구현할 수 있습니다
- 테두리와 CSS3를 결합하면 다양한形状을 만들 수 있습니다
- 요소를 생성할 때 DocumentFragment를 사용하면 성능이 향상됩니다【일괄 처리】
- ~~ 연산자는 정수 부분을 추출하는 간단한 방법입니다
- 경계값 제한에 Math.max와 Math.min 함수가 유용합니다
- 애니메이션에는 setInterval 또는 setTimeout을 사용합니다
- call 메소드를 사용하면 특정 객체의 컨텍스트로 함수를 실행할 수 있습니다
- 이벤트 위임을 통해 이벤트 리스너를 효율적으로 관리할 수 있습니다【부모 요소에 하나의 리스너】
- 배열을 순회할 때 길이를 미리 저장하면 성능이 개선됩니다