JavaScript 객체지향 프로그래밍 기초

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 메소드를 사용하면 특정 객체의 컨텍스트로 함수를 실행할 수 있습니다
  • 이벤트 위임을 통해 이벤트 리스너를 효율적으로 관리할 수 있습니다【부모 요소에 하나의 리스너】
  • 배열을 순회할 때 길이를 미리 저장하면 성능이 개선됩니다

태그: JavaScript OOP Prototype Object-Oriented function

8월 28일 02:41에 게시됨