상세 컨텐츠

본문 제목

자바스크립트 문법2(객체)

Web Development/Javascript

by thankee 2008. 9. 28. 15:39

본문

객체

자바스크립트에서도 객체를 사용하는 방법을 제공하고 있다. 하지만, 다양한 객체 관련 기술을 지원하는 듯 흉내내기일 뿐이다. 즉, Java 같은 객체 지향 언어에 있는 다양한 객체 관련 기술들과 권한 제한 등은 지원하지 않고, 객체를 선언해서 작성할 수 있는 기초적인 형태는 제공하고 있다. 실제로 자바스크립트의 객체는 일반적인 배열로서 단순하게 변수나 함수의 연결로 구성되어 있다고 볼 수 있다.

객체의 생성

객체를 바로 생성해서 사용하는 방법은 다음과 같다.

//객체를 생성하기 위한 가장 기초적인 방법
var obj = new Object();
obj.name = "Ivan";
obj.grade = ["A+", "B+", "F"];
obj.birth = new Date(1984,11,04);
obj.print = function()
{
    alert(
        this.name + "\n" +
        this.grade.toString() + "\n" + 
        this.birth.toString());
}
obj.print();

//JSON(Javascript Object Notation)을 이용한 방법
var myObj =
{
    name : "Son",
    grade : [99,87,43,87,56],
    date : new Date(),
    print : function()
    {
        document.write(
            this.name + "<br>" +
            this.grade.toString() + "<br>" +
            this.date.toString());
    }
};
myObj.print();

JSON방법은 좀 더 깔끔하고 구조적으로 표현할 수 있기 때문에 처음 객체를 선언할 때 많이 사용하고, 만들어진 객체를 수정할 때는, 첫번째 방법으로 수정하는 것이 간편하다. 하지만 이것은 정해진 것이 아니기 때문에 프로그래머가 원하는 방향으로 마음대로 선택해 작성할 수 있다.

클래스의 정의

위에서 봤던 같은 방법은, 일반적인 java에서 Class를 생성하고 instance를 선언하는 것과 달리, 바로 Instance에 해당하는 객체를 생성하는 방법이다. 실제로 Java나 C++ 등의 객체 지향 언어를 다룰 때, 클래스를 먼저 정의하고 그 인스턴스를 선언해서 사용하게 된다. 자바스크립트에서도 그런 문법을 지원하는데 다음과 같다.

//클래스 정의
function Person(strName, objBirth)
{
    //속성 정의
    var strBirth = objBirth.toString();
    this.name = strName;
    this.birth = strBirth;
    //메서드 정의
    this.print = function()
    {
         document.write(this.name + "<br>" + this.birth);
         alert("strBirth 변수 존재 여부 : " + strBirth);
    }
}
//인스턴스 선언
var person = new Person("Son", new Date(1984, 11, 04));
//인스턴스 사용
person.print();

하지만 위와 같이 클래스 정의 내부에서 메서드를 정의할 경우, Closure(클로저)를 생성하게 되는데 Closure가 생성될 때 나타나는 특징이 있다. Closure가 생성된 객체 내부에 존재하는 위의 strBirth라는 지역변수는 다른 지역변수 처럼 사용 후 바로 해제되지 않고 메모리에 계속 남아있게되며, 위의 print()라는 메서드에 의해서 계속해서 사용되어 질 수 있다.(하지만 외부에서 참조가능하다거나 다른 prototype메서드에 의해 참조되지 않는다.) 이 점을 이용해야 하는 경우도 있을 수 도 있지만 그렇지 않은 경우, 즉 Closure로 인해 지역변수가 메모리를 지속적으로 차지하는 것을 원하지 않는다면 클래스 내부에서 메서드를 정의하지 않고 다음처럼 외부에 정의하면 된다.

//클래스 정의
function Person(strName, objBirth)
{
    //속성 정의
    var strBirth = objBirth.toString();
    this.name = strName;
    this.birth = strBirth;
}
//메서드 정의
Person.prototype.print = function()

     document.write(this.name + "<br>" + this.birth);
     alert("strBirth 변수 존재 여부 : " + strBirth);
}
//인스턴스 선언
var person = new Person("Son", new Date(1984, 11, 04));
//인스턴스 사용
person.print();

위와 같이 메서드 정의를 외부로 빼버린다면, Closure가 생성되지 않아 지역변수는 지역변수로서의 역할을 하게 된다. 하지만 Closure가 항상 필요없는 존재는 아니며 상황에 따라 필요할 경우도 존재한다.

생성자

클래스를 정의할 때에 생성자를 자주 사용하게 되는데, 자바스크립트에서는 클래스를 정의하는 function 그 자체가 생성자다. 즉 아래의 'function Person(){ ... }' 함수 자체가 생성자이자, 클래스 몸체의 역할까지 하게 된다.

//클래스 정의이자 생성자 정의
function Person()
{
    alert("생성자 코드 작동");
}

var person = new Person();

Prototype

클래스의 속성과 메서드의 정의할 때, 생성자 안에 모두 기입하는 방법과 Prototype을 이용한 방법이 있다. 두가지 방법은 분명한 차이를 가지고 있고, 내부적으로 처리하는 방법도 다르다.

  1. 생성자에서 정의한 메서드와 속성은 인스턴스를 생성할 때마다, 각각 인스턴스의 메모리 공간에 복사된다. 함수라고 할지라도 각 인스턴스마다 복사되므로 메모리 공간을 더 차지하게 된다. Prototype을 이용해서 메서드나 속성을 정의했을 경우에는, 메모리에 단하나의 공간만 잡은채, 각각의 인스턴스에 링크를 걸어두는 방법을 사용한다.
  2. Prototype으로 정의한 속성이나 메서드라 할지라도, 사용자가 해당 속성, 메서드에 값을 할당하는 순간 Prototype과 연결이 끊어지고, 인스턴스의 메모리 공간에 따로 위치하게 된다.
    //클래스 : 정의
    function Person() { ; }
    //Prototype : 속성정의
    Person.prototype.name = "default Name";
    Person.prototype.age = "default Age";
    //Prototype : 메서드 정의

    Person.prototype.printName = function()
    {  
         alert("default Name function!!\n" + this.name );
    }
    Person.prototype.printAge = function()
    {
        alert("default Age function!!\n" + this.age);
    }

    //인스턴스 : 선언
    var person = new Person();
    //인스턴스 : 사용
    person.printName();
    person.printAge();

    //인스턴스 : name 속성과 printName 메서드 할당
    person.name = "Son";
    person.printName = function()
    {
        alert("defined Name function\n" + this.name);
    }

    //Prototype : 모든 속성과 메서드 재정의
    Person.prototype.name = "redefined name";
    Person.prototype.age = "redefined age";
    Person.prototype.printName = function()
    {
        alert("redefine Name function!!\n" + this.name);
    }
    Person.prototype.printAge = function()
    {
        alert("redefine Age fucntion!!\n" + this.age);
    }

    //인스턴스 : 사용
    person.printName();
    person.printAge();
    위의 소스를 실행해보면, 중간에 Prototype으로 변경한 내용이 age속성과 printAge 메서드에만 반영된다는 것을 알 수 있다. 이유인 즉슨, 도중에 인스턴스의 name속성과 printName메서드의 값을 따로 할당 해버렸기 때문이다. 이렇게 인스턴스에서 Prototype으로 지정된 개체의 값을 할당해버리면 Prototype과 연결이 끊어지고 인스턴스 내부에 따로 저장이 된다. 반면에, 인스턴스의 age, printAge()는 할당되지 않았기 때문에 여전히 Prototype과 연결이 되어 있기 때문에, Prototype의 속성과 메서드를 수정 했을때 변경사항이 그대로 인스턴스에 반영된다.
  3. Prototype을 이용하면 어떤 순간에도 속성과 메서드를 정의하고 수정할 수 있다. 심지어 생성자 이전에도 가능하다.(메서드만 가능. 속성은 생성자 이전에 Prototype으로 정의할 수 없다.) 반면에 생성자를 이용하여 정의한 속성과 메서드는 한번 정의되면 수정할 수 있는 방법이 없다.
  4. 생성자에서 정의한 멤버(속성, 메서드)와 Prototype로 정의한 멤버의 이름이 동일 할 경우에는 생성자에서 정의한 것이 우선한다. 따라서 생성자에서 정의된 메서드나 속성의 기본값 등은, Prototype으로 변경할 수 없다.
  5. Prototype으로 할당된 속성은 해당 클래스의 모든 인스턴스들이 공유할 수 있으므로, 객체간의 통신수단으로 활용 될 수 있다.

상속(Inheritance)

자바스크립트에서 상속은 prototype를 이용하며, 메서드 재정의 등 대부분의 기능을 활용할 수 있다.

//Person 클래스 정의
function Person()
{
    this.name = "";
    this.age = 0;
    this.print = function()
    {
        alert("name : " + this.name + "\nage : " + this.age); 
    }
}

//Student 클래스 정의
function Student()
{
    this.school = "";
    //메서드 재정의
    this.print = function()
    {
        alert("name : " +
            this.name + "\nage :" +
            this.age + "\nschool : " +
            this.school);
    }
}

//Others 클래스 정의
function Others()
{
    //다른 특이사항을 정의하지 않고 상속을 받는다.
}

//Person 클래스 상속
Student.prototype = new Person();
Others.prototype = new Person();

//객체 선언
var
person = new Student();
var otherPeople = new Others();

person.name = "Son";
person.age = 12;
person.school = "Buk-gun elementary school";
person.print();

otherPeople.name = "Jung";
otherPeople.age = 14;
otherPeople.print();

솔직히 말하자면, 상속이라는 기능 역시 흉내내기에 불과하다. 자바스크립트에서 제공하는 기능을 활용하여 상속하는 것처럼 보이게 하는 것일 뿐이다. 실제로 자바스크립트를 활용해서 객체 지향 프로그래밍(굳이 객체 지향 프로그래밍이이 아니더라도..)을 하기 위해서는 수많은 위험 인자를 감수하고 진행해야 한다. 중소규모 프로젝트에서는 개발자끼리 규정을 정해두고 서로 확실히 지키는 방법으로 구현할 수 있겠지만, 대규모 프로젝트에서는 이런 것조차 힘들어 진다. 따라서, 설계대로 객체가 생성되었는지, 변수 형 타입은 제대로 되었는지, 원하는 메서드는 존재하는지 검사하는 등의 제약사항을 자바스크립트에 의존하지 않고 스스로 프로그래밍하여 적용하는 방법들도 사용되고있다고 한다.

Static 멤버

Static 멤버 역시 흉내낼 수 있다. 클래스를 정의하기 위해서 만드는 function은 그 자체로 function객체이다. 따라서 function 객체에 사용자가 속성을 추가함으로서 Static 멤버처럼 사용하는 것이다.

//클래스 정의
function Person()
{
    this.name;
    this.age;
    this.number;
}
Person.prototype.set = function(name, age)
{
    this.name = name;
    this.age = age;
    this.number = ++Person.total;
}
Person.prototype.print = function()
{
    alert("Name : " + this.name
        + "\nAge : " + this.age
        + "\nPerson ID : " + this.number
        + "\nTotal : " + Person.total);
}

//클래스 변수 지정
Person.total = 0;

//객체 생성 및 사용
var person1 = new Person();
var person2 = new Person();

person1.set("Ko", 23);
person2.set("Jun", 22);

person1.print();
person2.print();

개인적인 생각을 말하자면, 자바스크립트로 대부분의 객체지향 기능을 흉내낼 수 있지만, 내부적으로 동작하는 방법을 이해한다면 객체지향 프로그래밍을 쉽게 따라할 수 있을 뿐만아니라, 문제 해결에서도 큰 도움이 될것이라 믿는다.

관련글 더보기