http://m.blog.naver.com/PostView.nhn?blogId=hopefuldream&logNo=140110566940 에서 참조

XML은 익히 많이 들어봐서 대충~ 무엇이다라는건 알고 있지만 확실히 무엇인지..
DTD는 무엇인지 스키마는 무엇인지 잘 모르는 분들이 계실겁니다.

XML은 정해진 데이터 표현법이라고 볼 수 있습니다. 생긴모습은
<booklist>
    <book id=“스트럿츠1”>
        <저자>홍길동</저자>
        <가격>10000원</가격>
    </book>
    <book id=“스프링”>
        <저자>김갑순</저자>
        <가격>20000원</가격>
    </book>
</booklist>
이렇게 생겼습니다.. HTML과도 비슷하게 생긴 도데체 이게 뭐 어쨌다는거냐…라고 물으신다면..
제가 상상한 다음과 같은 이야기를 해야할것 같습니다.

서로다른 업체 혹은 프로그램끼리 데이터를 주고받는 방법은 사실 매우 많습니다. 예를들어, 김개발씨가 통합서점프로그램을 만들면서
홍길동씨에게 이렇게 말합니다.
“야, 길동아. 나 이번에 통합서점프로그램 만드는 중이니까, 너네 서점 책목록좀 줘봐. 내가 정해준 형태로 줘야 프로그램에서
분석해 쓰거든? 내가 정한 형태로 메모장에 작성해서 텍스트 형태로 줘”
이렇게 말합니다. 김개발씨는 텍스트문서에

책=스트럿츠, 스프링, 아이바티스
가격=10000, 20000, 30000
저자=홍길동, 김갑순, 슈아

이런식으로 써서 주면, 프로그램에서 파일을 읽어와 데이터를 파싱해서 각각 알맞게 DB에 넣도록 프로그램을 짰던 것입니다.
그리고 이번엔 금당무씨에게 똑같이 말했습니다.
“당무야.. 나 이번에 통합서점프로그램 만드는 중인데…”
그런데 이 당무씨는 순순히 OK 하지 않고 반항을 합니다.
“야임마~ 나 이미 우리 서점프로그램에서 쓰는 데이터 형식 있으니까 그거 갔다가 니가 알아서해라”
그리고선
책{스트럿츠, 홍길동, 10000}
책{스프링, 김갑순, 20000}
이런 형식의 텍스트문서를 던져 줍니다…

무슨 이야기 하는지 아실겁니다. 데이터를 주고받을때 모두 똑같은 형식으로 주고받으면 … 각 프로그램마다 파싱하는 부분을
따로 만들 필요가 없겠지요. 그런데 데이터형식을 회사끼리도 아니고 전세계인이 똑같은 방식으로 만들어 주고받는다??
이게 가능한가?? 가능합니다. XML 이 그런 용도로 쓰입니다.

그렇다면 데이터 주고받을려고 XML만들었나? 저는 그렇다고 생각합니다. 데이터 주고받을일 없으면.. 나혼자 개발하고 혼자 지지고볶고
다 한다면 XML따위는 필요 없다고 생각합니다. 나 혼자 꼴리는대로 데이터 주고받게 만들면 그만이고..
실제 흔히 학원에서 자바 메신져 프로그램 만들기를 마지막 과제로 하는데…
이때 메신져간 데이터를 주고받는 형식을 보면 개발자 맘대로 정하는 경우가 많습니다.

그럼 그 XML 은 어떻게 만드냐??? 그냥 메모장 열어서 다짜고짜
<booklist>
    <book>
    </book>
</booklist>
이렇게 만들고 확장자 xml로 저장하면 그냥 XML문서가되는거냐?? 라고 한다면 네.. 그렇습니다…
특수문자에 대한 규칙이나.. “<” 다음에 공백이 와서는 안된다는 등의 기초적인 규칙만 지켜주면 그게 XML 입니다.
안에 들어가는 booklist 등의 문자를 내맘대로 만들수가 있는 겁니다.
<책목록>
  <책제목></책제목>
</책목록>
이렇게 만들어도 이것도 맞는 XML 인 겁니다.  이렇게 내 맘대로 만들어서 상대방한테 여기 맞춰 데이터 달라고 하면 되는거고
아니면 맘대로 이런 XML형식으로 데이터 줄테니까 맘대로 써라.. 할수도 있는 것입니다.

이렇게 간단하면 DTD는 뭐고 스키마는 뭐냐…
결론은 대중성이라고 생각합니다… 즉, 내가 만든 프로그램을 홍길동씨도 쓰고 김갑순씨도 쓰고 미국에사는 제임스씨도 쓴다는것이죠.
내가 분명
<책목록>
  <책제목></책제목>
</책목록>
이런식으로 데이터를 달라고 했는데, 오지마을에사는 김순덕씨는
<책리스트>
  <책타이틀></책타이틀>
</책타이틀>
이렇게 주는겁니다. 이런사람이 안생길까요? 절대로 생깁니다. 특히 공개된 대중적인 프로그램이라면..
예를들어 RSS 같은경우 RSS 형식에 맞게 데이터를 표현해야 RSS리더 프로그램에서 RSS를 읽습니다.
그런데 아랫층사는 박판돌씨는 그냥 멋대로 XML을 만들고 RSS리더 프로그램에서 RSS를 읽히려고 합니다.

그래서 아예 내가 원하는대로 XML을 표현하게 규제를 해합니다.. 그게 DTD, 스키마 입니다…
DTD 가 최초 XML 형식을 정의하는 언어였는데 그게 개선된것이 스키마 구요.. 둘다 목표는 같은겁니다…
데이터정의언어… 왜? 규제할라고.김순덕씨같은 사람들때문에…

에이..난 대중에 공개할일도 없고 개발자는 나 혼자인데요???
그럼 DTD고 스키마고 나발이고간에 만들필요 없습니다. 그냥 XML문서 내 맘대로 만들고 프로그램에서 돌리면 됩니다.

그럼 DTD나 스키마 쓴거랑 안쓴거랑 어떻게 구분하고 규제하고 하는가???

XML문서의 초기부분에..
<?xml version=“1.0” encoding=“euc-kr” ?>
<booklist>
    <book>….
이렇게만 쓰면.. 규제고 뭐고 하는거 없고 그냥 내맘대로 XML형식에만 어긋나지 않게 만들면 됩니다만…
<?xml version=“1.0” encoding=“euc-kr” ?>
<booklist xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
                      xsi:noNamespaceSchemaLocation=“test.xsd”>
이렇게 덧붙여서 시작하면..test.xsd 에서 선언한대로 xml 문서를 만들어야 합니다… 틀리면…
XML문서를 읽어들이는 프로그램의 파서가 에러를 내뱉습니다..
XML문서를 읽어들이는 프로그램으로 우리는 주로 인터넷 익스플로러를 사용하지요… 이 인터넷 익스플로러 안에
XML을 파싱하는 프로그램이 내장되어 있습니다.. 요놈이 “에러야!!크흑!!!!컥!!!” 하면서 에러를 뱉어냅니다..

http://m.blog.naver.com/PostView.nhn?blogId=hopefuldream&logNo=140110566940 에서 참조

XML은 익히 많이 들어봐서 대충~ 무엇이다라는건 알고 있지만 확실히 무엇인지..
DTD는 무엇인지 스키마는 무엇인지 잘 모르는 분들이 계실겁니다.

XML은 정해진 데이터 표현법이라고 볼 수 있습니다. 생긴모습은
<booklist>
    <book id=“스트럿츠1”>
        <저자>홍길동</저자>
        <가격>10000원</가격>
    </book>
    <book id=“스프링”>
        <저자>김갑순</저자>
        <가격>20000원</가격>
    </book>
</booklist>
이렇게 생겼습니다.. HTML과도 비슷하게 생긴 도데체 이게 뭐 어쨌다는거냐…라고 물으신다면..
제가 상상한 다음과 같은 이야기를 해야할것 같습니다.

서로다른 업체 혹은 프로그램끼리 데이터를 주고받는 방법은 사실 매우 많습니다. 예를들어, 김개발씨가 통합서점프로그램을 만들면서
홍길동씨에게 이렇게 말합니다.
“야, 길동아. 나 이번에 통합서점프로그램 만드는 중이니까, 너네 서점 책목록좀 줘봐. 내가 정해준 형태로 줘야 프로그램에서
분석해 쓰거든? 내가 정한 형태로 메모장에 작성해서 텍스트 형태로 줘”
이렇게 말합니다. 김개발씨는 텍스트문서에

책=스트럿츠, 스프링, 아이바티스
가격=10000, 20000, 30000
저자=홍길동, 김갑순, 슈아

이런식으로 써서 주면, 프로그램에서 파일을 읽어와 데이터를 파싱해서 각각 알맞게 DB에 넣도록 프로그램을 짰던 것입니다.
그리고 이번엔 금당무씨에게 똑같이 말했습니다.
“당무야.. 나 이번에 통합서점프로그램 만드는 중인데…”
그런데 이 당무씨는 순순히 OK 하지 않고 반항을 합니다.
“야임마~ 나 이미 우리 서점프로그램에서 쓰는 데이터 형식 있으니까 그거 갔다가 니가 알아서해라”
그리고선
책{스트럿츠, 홍길동, 10000}
책{스프링, 김갑순, 20000}
이런 형식의 텍스트문서를 던져 줍니다…

무슨 이야기 하는지 아실겁니다. 데이터를 주고받을때 모두 똑같은 형식으로 주고받으면 … 각 프로그램마다 파싱하는 부분을
따로 만들 필요가 없겠지요. 그런데 데이터형식을 회사끼리도 아니고 전세계인이 똑같은 방식으로 만들어 주고받는다??
이게 가능한가?? 가능합니다. XML 이 그런 용도로 쓰입니다.

그렇다면 데이터 주고받을려고 XML만들었나? 저는 그렇다고 생각합니다. 데이터 주고받을일 없으면.. 나혼자 개발하고 혼자 지지고볶고
다 한다면 XML따위는 필요 없다고 생각합니다. 나 혼자 꼴리는대로 데이터 주고받게 만들면 그만이고..
실제 흔히 학원에서 자바 메신져 프로그램 만들기를 마지막 과제로 하는데…
이때 메신져간 데이터를 주고받는 형식을 보면 개발자 맘대로 정하는 경우가 많습니다.

그럼 그 XML 은 어떻게 만드냐??? 그냥 메모장 열어서 다짜고짜
<booklist>
    <book>
    </book>
</booklist>
이렇게 만들고 확장자 xml로 저장하면 그냥 XML문서가되는거냐?? 라고 한다면 네.. 그렇습니다…
특수문자에 대한 규칙이나.. “<” 다음에 공백이 와서는 안된다는 등의 기초적인 규칙만 지켜주면 그게 XML 입니다.
안에 들어가는 booklist 등의 문자를 내맘대로 만들수가 있는 겁니다.
<책목록>
  <책제목></책제목>
</책목록>
이렇게 만들어도 이것도 맞는 XML 인 겁니다.  이렇게 내 맘대로 만들어서 상대방한테 여기 맞춰 데이터 달라고 하면 되는거고
아니면 맘대로 이런 XML형식으로 데이터 줄테니까 맘대로 써라.. 할수도 있는 것입니다.

이렇게 간단하면 DTD는 뭐고 스키마는 뭐냐…
결론은 대중성이라고 생각합니다… 즉, 내가 만든 프로그램을 홍길동씨도 쓰고 김갑순씨도 쓰고 미국에사는 제임스씨도 쓴다는것이죠.
내가 분명
<책목록>
  <책제목></책제목>
</책목록>
이런식으로 데이터를 달라고 했는데, 오지마을에사는 김순덕씨는
<책리스트>
  <책타이틀></책타이틀>
</책타이틀>
이렇게 주는겁니다. 이런사람이 안생길까요? 절대로 생깁니다. 특히 공개된 대중적인 프로그램이라면..
예를들어 RSS 같은경우 RSS 형식에 맞게 데이터를 표현해야 RSS리더 프로그램에서 RSS를 읽습니다.
그런데 아랫층사는 박판돌씨는 그냥 멋대로 XML을 만들고 RSS리더 프로그램에서 RSS를 읽히려고 합니다.

그래서 아예 내가 원하는대로 XML을 표현하게 규제를 해합니다.. 그게 DTD, 스키마 입니다…
DTD 가 최초 XML 형식을 정의하는 언어였는데 그게 개선된것이 스키마 구요.. 둘다 목표는 같은겁니다…
데이터정의언어… 왜? 규제할라고.김순덕씨같은 사람들때문에…

에이..난 대중에 공개할일도 없고 개발자는 나 혼자인데요???
그럼 DTD고 스키마고 나발이고간에 만들필요 없습니다. 그냥 XML문서 내 맘대로 만들고 프로그램에서 돌리면 됩니다.

그럼 DTD나 스키마 쓴거랑 안쓴거랑 어떻게 구분하고 규제하고 하는가???

XML문서의 초기부분에..
<?xml version=“1.0” encoding=“euc-kr” ?>
<booklist>
    <book>….
이렇게만 쓰면.. 규제고 뭐고 하는거 없고 그냥 내맘대로 XML형식에만 어긋나지 않게 만들면 됩니다만…
<?xml version=“1.0” encoding=“euc-kr” ?>
<booklist xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
                      xsi:noNamespaceSchemaLocation=“test.xsd”>
이렇게 덧붙여서 시작하면..test.xsd 에서 선언한대로 xml 문서를 만들어야 합니다… 틀리면…
XML문서를 읽어들이는 프로그램의 파서가 에러를 내뱉습니다..
XML문서를 읽어들이는 프로그램으로 우리는 주로 인터넷 익스플로러를 사용하지요… 이 인터넷 익스플로러 안에
XML을 파싱하는 프로그램이 내장되어 있습니다.. 요놈이 “에러야!!크흑!!!!컥!!!” 하면서 에러를 뱉어냅니다..

http://nefariousdesigns.co.uk/object-oriented-javascript.html 에서 참조

Object-Oriented Javascript

Posted Wednesday 24th May, 2006

Update October 2010: I’m currently following up this article with some notes about development method, and then again with a more in-depth look at current practices for object-oriented JavaScript. Hopefully, this should serve to correct some of my errors in this older article. Once you’ve finished reading this article, please read the following:

Following on from my earlier post, ”Object-Oriented Concepts,” it’s time we started to have a look at some examples of execution. I’m going to start with Javascript because I believe this to have widest appeal – PHP, as a server-side language, is probably of interest to fewer developers so I’ll cover it later.

So without further ado, here’s how to objectify your javascript…

Objects in Javascript

Although Javascript shouldn’t be classed as an object-oriented language, pretty much everything within it is object based; from DOM scripting (Document Object Model) through to specific built-in objects such as Image and Date. This basically means that we can adopt some OOP concepts but not all.

Javascript handles objects in a number of ways; a developer can define an object and then instantiate with the new operator, a developer can declare an object on the fly using the object literal or a developer can extend an existing object (either built-in or user-defined) using its prototype.

In fact, even data-types that can be declared literally, such as arrays and strings, can also be declared as objects. This is mainly so that object methods can be applied to literal values when needed. Javascript does this by temporarily converting your literal into an object. A good example would be the String.lengthmethod.

Sounding complicated? Don’t worry, it’s really very easy. Let’s start getting our hands dirty…

The Object Literal

In programmer-speak, a “literal” is any value declared literally. Good examples would be string literals, array literals and boolean literals – the literal is the part after the “=” assignment operator:

// String literal
var my_string = "2468 This is a string";

// Array literal
var my_array = ['element1', 'element2', 'elephant'];

// Boolean literal
var my_boolean = true;

As a developer, you probably use these methods day-in, day-out; it still amazes me, however, how many programmers I speak to that don’t know the correct terminology!

In Javascript, we can also declare objects as a literal:

function getArea(radius)
{
  // return the radius of our circle using the
  // PI attribute of the built-in Math object
  return (radius * radius) * Math.PI;
}

var circle = {
  radius : 9,
  getArea : getArea(this.radius)
};

Here we’ve defined an object, circle, with a radius of 9. If we wanted to obtain the area of our circle, we could use the following line of code:

var my_radius = circle.getArea();

Fantastic! We’ve got ourselves a circle; but what happens when we want to declare more circles, all with varying radii?

Imagine we want to create lots of circle objects using our object as a base – if we’ve declared using the object literal, we’re not able to do that! To a certain extent, that’s the difference between objects and classes(see my earlier post); if we want our object to behave more like a class (which, unfortunately, aren’t really supported in Javascript even though we can mimic the behaviour), we need to use objects that utilise the new operator.

Note: For more information on the object literal, I recommend reading Chris Heilmann’s ”Show Love to The Object Literal,” and Dustin Diaz’s ”JSON for the Masses.”

The new Operator

The new operator creates an instance of any built-in or user-defined object – basically allowing us to re-use a user-defined obect (much like the behaviour of a class). Here are a couple of examples using built-inobjects:

// Create a date object
var obj_today = new Date();

// Preload an image using the Image object
var obj_supper = new Image();
obj_supper.src = 'thelastsupper.jpg';

Take note that Javascript’s built-in objects do not always use a proper class interface – ie. you can set attributes without using a method. In most OOP languages this is considered bad practice and you can often prevent it with the use of access modifiers (public, private, protected). Even though Javascript lets us get away with this method, when using its built-in objects, I personally tend to write a proper interface for my own objects.

Ok, so that’s how you declare using the built-in objects, but what about something user-defined?

When using the new keyword to declare user-defined objects, the objects require a constructor. Object structure (including an object’s constructor) is handled somewhat differently to most languages in Javascript. Here’s the syntax for defining this type of object:

function circle(radius)
{
  this.radius = radius;
  this.getArea = getArea;

  // Don't forget that a constructor should
  // always retain a valid state - so it should
  // always return true
  return true;
}

function getArea()
{
  // return the radius of our circle using the
  // PI attribute of the built-in Math object
  return (this.radius * this.radius) * Math.PI;
}

In the example above, you’ll notice that the syntax is almost completely identical to declaring a function. This function is the constructor of our object and attributes and methods are declared within it, using the this keyword.

Notice the Math object doesn’t need to be instantiated before we can use it – this is simply because it’s special and is always available for use.

We can now instantiate our object using the new operator as before:

var obj_pizza = new circle(9);

And we can work out the area of our circle using the following line of code:

var flt_area = obj_pizza.getArea();

Encapsulation

There is one problem with the above definitions of an object, however, and that’s the lack of encapsulation. If we were to include another piece of code that used a getArea() function (for instance a triangle object), our circle object’s getArea() method would be over-written. To better encapsulate our methods, we can define our objects either literally like so:

var circle = {
  radius : 9,
  getArea : function()
  {
    return (this.radius * this.radius) * Math.PI;
  }
};

Or non-literally like so:

function circle(radius)
{
  this.radius = radius;
  this.getArea = function()
  {
    return (this.radius * this.radius) * Math.PI;
  };

  return true;
}

In both cases this limits the scope of each getArea() method to each circle object.

Inheritance and prototype

The prototype property is a useful feature in Javascript – it basically allows us to add attributes or methods to an object. This is a form of inheritance.

As a quick example of prototype in use, let’s add a function to work out circumference:

function circle(radius)
{
  this.radius = radius;
  this.getArea = function()
  {
    return (this.radius * this.radius) * Math.PI;
  };

  return true;
}

// Add our new method
circle.prototype.getCircumference = function()
{
  return this.radius * Math.PI * 2;
}

// Instantiate our object
var my_pizza = new circle(9);

// Call our new method
var flt_pizza_circ = my_pizza.getCircumference();

“But wait, ” I hear you cry, “what if we want to inherit an entire object?”

Not a problem, prototype can handle this like so:

// Declare two objects - we're going to want Lion to
// inherit from cat
function cat()
{
  this.eyes = 2;
  this.legs = 4;
  this.diet = 'carnivore';

  return true;
}

function lion()
{
  this.mane = true;
  this.origin = 'Africa';

  return true;
}

// Now comes the inheritance
lion.prototype = new cat();

// We can now obtain lion.diet
var simba = new lion();
var simba_diet = simba.diet;

Ok, so now that we understand inheritance (you do understand, right?), let’s take a look composition.

Composition

Association

Association, in Javascript, is pretty straight forward. Here’s an example:

// Define an object
function brick()
{
  return true;
}

// Define an object
function wall()
{
  this.brick1 = new brick();
  this.brick2 = new brick();
  this.brick3 = new brick();

  return true;
}

Just like the pseudo-code example in my earlier post, we can see that the object “wall” now contains three instances of the “brick” object. This is association – the wall object “has” three bricks.

Now let’s take a look at aggregation.

Aggregation

Aggregation, as a relationship, is reliant on the ability to pass object and function parameters by reference. This basically means that a parameter represents a pointer to the actual object passed and not just a copy. Thankfully, Javascript handles all parameters in this way – which makes our aggregation relationship nice and easy:

function person
{
  return true;
}

function car(driver)
{
  this.driver = driver;

  return true;
}

var me = new person();
var myMotor = new car(me);

This relationship allows us to “use” the “me” object within our “myMotor” object. We could also use the “me” object in any other objects that require it – and we’d only need that single instantiation. This is immensely helpful when we’re using objects that control behaviours such as XmlHttpRequest.

Update: Thanks to Jonathan Snook, I’ve recently discovered that Javascript doesn’t pass all parameters by reference – in fact working out how Javascript has handled your parameter can be quite confusing. In our particular case, the parameter is passed by reference because it is an object. When we’re not working with objects, this might not be the case. For more detailed information, please take a look at Jonathan’s excellent article, ”Javascript: Passing by Value or by Reference.”

Update 2: Stefan Van Reeth’s fantastic comment, below, looks at references in a little more detail. I definitely recommend reading it as his explanation and examples may clear up confusion.

Polymorphism

Due to its object-based nature, Javascript handles polymorphism very well. Take a look at the following example:

function Person() {
  //...
  this.Speak = function() {
    // ...
  }
}

function Employee() {
  // ...
  this.Speak = function() {
    // ...
  }
}

AND (!!!)

Employee.prototype = new Person(); // inheritance
user emp = new Employee();
emp.Speak();

Employee inherits from Person and, as a result, already has a Speak() method. Because we want the Speak() method in Employee to do something different, we overload it with a new method definition. This is polymorphism in action.

Update: My original example for Polymorphism was entirely incorrect. Thanks go to “Joe is all you need to know” for pointing out my error and supplying this correct example.

Summary

So that’s a brief guide to adopting OOP practices in Javascript; I think you’ll agree that it adds real power when dealing with complicated scripts. However, that raises a common issue with OOP – you shouldn’t just use it for the sake of it. In fact, I often find that developers who are using these practices for everything are often the same developers who don’t understand the concepts fully.

My next OOP post will be regarding object-oriented PHP but, after an inquisitive email from Nate Logan, I also intend writing a piece about abstraction – the practice of reducing a process down to it’s root concepts and modelling them in the programming language of your choice.

소개

이전 글 에서 함수의 컨셉에 대해 소개했다. 함수를 사용함으로서 프로그램들을 논리적인 덩어리들로 쪼개서 당신의 코드를 좀더 잘 조직화하고, 쉽게 재사용할 수 있었다. 이제 당신이 자바스크립트 프로그래밍의 핵심적인 개념들을 거부감 없이 받아들일 수 있게 되었으므로, 객체 에 대해 소개함으로서 활용의 폭을 더욱 넓혀보고자 한다. 객체를 사용함으로서 함수로 정의한 기능성들을 하나로 묶을 수 있고, 또한 그것을 이곳저곳으로 전달하며 또한 참조할 수 있다. 이러한 능력은 당신이 앞으로 작성할 코드에 상당히 실질적인 의미를 갖게 될 것이다 – 비록 지금 이 순간에는 다소 모호하게 들리겠지만 말이다.

눈치채지 못했을수도 있지만, 당신은 이 시리즈를 따라 오면서 암묵적으로 객체들을 사용해 왔다. 이제 이것에 대해 좀 더 명시적인 설명을 함으로서 자바스크립트에서 객체가 어떻게 동작하는지, 그리고 객체를 통해 당신의 프로그램을 좀 더 명확하게 하고 재사용하기 쉽도록 하는 방법을 알려주겠다.

이 글의 구조는 다음과 같다:

  • 왜 객체를 사용하는가?
  • 익숙한 영역
  • 객체 생성하기
  • 자가 참조
  • 연관 배열로서의 객체
  • 객체 리터럴
  • 요약 – 배울것이 더 많이 있다
  • 읽어볼것들
  • 연습문제
다운로드하거나 직접 실행해볼 수 있는 예제가 준비되어 있다. 이것은 삼각형의 면적을 계산하는 코드를 포함하고 있는데, 객체를 사용한 것과 사용하지 않은 것이 있다. 이 코드는 아래에서 설명하는 예제들로 만들어져 있다. 삼각형 객체 예제를 실행해보라.

왜 객체를 사용하는가?

객체에 대해 주의를 기울이는 단 하나 가장 중요한 이유는, 객체를 사용함으로서 당신의 코드에서 데이터와 처리과정을 더 잘 표현할 수 있기 때문이다. 익숙한 예제로서, 당신이 삼각형에 관한 뭔가를 코딩한다고 가정하자. 삼각형은 3개의 변 을 가지고 있다는 것을 알고 있으므로, 특정한 삼각형에 대해 다루려면 당연히 3개의 변수를 만들어야 할 것이다:

  1. // This is a triangle.
  2. var sideA = 3;
  3. var sideB = 4;
  4. var sideC = 5;

자, 삼각형이 준비되었다. 하지만 아직 명확하지는 않다. 따로따로 추적해야 할 세개의 변수를 이제 만들었고, 당신이 이것을 가지고 무엇을 어떻게 하려고 했었나를 떠올리게 해 줄 주석이 있다. 이것은 그다지 명확하거나, 사용할만하다고 할 수는 없다. 어쨌든, 계속해보자. 이 “삼각형”에 대해 어떤 계산을 할 수 있을까? 면적을 알기 위해서, 다음과 같은 함수를 사용할 수 있을 것이다:

  1. function getArea( a, b, c ) {
  2. // Heron의 공식을 이용해서 삼각형의 면적을 계산한다.
  3. var semiperimeter = (a + b + c) / 2;
  4. var calculation = semiperimeter * (semiperimeter – a) * (semiperimeter – b) * (semiperimeter – c);
  5. return Math.sqrt( calculation );
  6. }
  7. alert( getArea( sideA, sideB, sideC ) );

뭔가 계산을 하기 위해서는 삼각형에 대한 모든 정보를 전달해주어야 한다는 것을 깨달았을 것이다. 삼각형과 관련된 활동(계산)은 삼각형의 데이터로부터 완전히 분리되어 있는데, 이러한 고립에는 그다지 의미가 있어 보이지 않는다.

좀 더 보자. 나는 이 함수와 변수들에 대해 상당히 범용적인 이름을 정해주었다: getArea, sideA 와 같이 말이다. 다음주에, 직사각형에 대해서 동작하도록 프로그램을 확장해야 함을 깨달았다면 어떻게 할 것인가? 아마도, 사각형에 사용하기 위해 sideA 와 sideB 같은 변수를 사용하려고 하겠지만, 이러한 변수는 이미 사용되었다. side1 과 side2 를 사용할수도 있겠지만, 이런 변수를 사용함으로서 혼동과 재난을 초래할 것이 분명함을, 당신도 이미 짐작할 것이라고 생각한다. 아마도, rectangleSideA, rectangleSideB 같은 변수들을 시도해 보다가 모순 속에 빠져들게 될 것 같다. 삼각형에 대해 이미 만들어 둔 코드를 다시 수정해서 triangleSideA 같은 것을 쓰게끔 하다가, 필경 에러를 만들어 낼 것이다. 이러한 수작업과 오류는 물론 함수의 이름에도 계속된다 – 두개의 다각형에 대해서, 그 계산이 개념적으로는 같은 것이기 때문에, 똑같이 getArea 라는 이름을 사용하고 싶지만, 그렇게 할 수 없다. 데이터를 표현할 수 있는 더 좋은 방법이 있을것이다.

몇개의 명령어들을 묶어서 단일한, 적당한 이름을 가진 활동(함수)을 만들어 내는 것이 이치에 맞는다. 마찬가지로, 몇가지 “것” 들을 하나의 단위로 묶어서 객체를 만들어내는 것이 이치에 맞는다. 자바스크립트에서 미리 정의된 기초적 데이터 타입(문자열, 상수, 불린 등)에 제한받는 대신, 객체를 만들어 사용함으로서 당신만의 데이터 타입 연결을 만들어 낼 수 있다. 객체에 사용되는 변수들은 숫자나 타입의 제한을 받지 않는다. 이렇게, 형식에 얽매이지 않는 유연함은, 프로그램을 작성할 때 당신이 다루려고 하는 것을 직접적으로 다루는 구조를 만들어낼 수 있게 하며, 또한 그것들을 자바스크립트에 내장된 기초적인것들과 마찬가지로 사용할 수 있게 해준다. 이제 삼각형과 직사각형 객체를 만들 것인데, 각각의 객체는 그 모양을 다루기 위해 필요한 모든 데이터들을 포함할 것이며, 또한 그 데이터들을 가지고 수행하려고 하는 모든 행동들도 함께 포함할 것이다. 이러한 목적을 염두에 두고, 문법을 보도록 하자.

익숙한 영역

이전 글의 마지막 실습을 보면, 다음과 같은 코드가 포함되어 있다:

  1. var obj = document.getElementById( elementID );

그리고:

  1. obj.style.background = ‘rgb(’+red+’,’+green+’,’+blue’)’;

자, 객체가 무엇인지 몰랐지만 이미 객체를 사용하고 있었다. 이 두개의 조각을 좀 더 자세히 분석해서 자바스크립트의 객체 문법에 대해 살펴보도록 하자.

var obj = document.getElementById( elementID ) 이 코드는 익숙해 보인다. 명령어 마지막에 포함되어 있는 괄호는 뭔가가 실행될 것이라는 의미이고, 이 실행의 결과가 obj 라는 변수에 저장될 것이라고 짐작할 수 있다. 여기에서 새로운 것은 딱 하나, 중간에 있는 점 이다. 이러한 점 표기법은 자바스크립트가 객체 안에 저장된 데이터에 접근하기 위해 사용하는 방법이다. 점 은 + 나 – 와 마찬가지로, 피연산자 사이에 있는 연산자이다.

관습적으로, 객체 안에 저장되고 점 연산자를 통해 접근하는 데이터를 property 라고 부른다. 프로퍼티는 함수가 될 수 있는데, 이 경우 method 라 부른다. 이러한 단어에 특별한 의미는 없다 – 메서드는 함수일 뿐이고, 프로퍼티는 변수일 뿐이다.

점 연산자의 왼쪽에는 객체가, 오른쪽에는 프로퍼티가 놓인다. 위의 코드에 이러한 규칙을 적용해 본다면, 자바스크립트에 내장되어 있는 document 객체의 getElementById 메서드에 접근한다고 말할 수 있을 것이다. 이것에 관해 더 자세한 내용은 이어질 DOM 여행 글에서 확인할 수 있다.

다음 코드는 조금 더 흥미롭다: 이것은 점을 두개 가지고 있다. 자바스크립트의 객체 지원 중에서 정말로 흥미로운 것 중 하나는, 점 연산자 체인을 통해 복잡한 구조 속으로 들어간다는 것이다. 짧게 말해서, 이러한 체인은 마치 var x = 2 + 3 + 4 + 5; 를 계산하고 값으로 14를 기대하는 것과 같다. 객체 참조는 자신을 반환하고, 이러한 체인이 왼쪽에서 오른쪽으로 계속 이어진다(친구들에게 이것을 설명하면서 자바스크립트가 점 연산자를 “좌측 연결 삽입 연산자” 로 사용한다고 표현해서 깊은 인상을 줄 수 있을 것이다) 이 경우, obj.style 이 먼저 평가되고, 이것을 해석하면 그것의 background 프로퍼티에 접근하는 객체가 된다. 당신이 원한다면, 괄호를 사용해서 이것을 좀 더 명시적으로 표현할 수 있다: (obj.style).background.

객체 생성

삼각형 객체를 만들기 위해, 다음의 문법을 사용해서 명시적으로 생성할 것이다:

  1. var triangle = new Object();

triangle 는 이제 비어있는 기초이며 무언가를 그 위에 쌓아올려야 한다. 점 연산자를 통해 프로퍼티들을 추가함으로서 그렇게 할 수 있다:

  1. triangle.sideA = 3;
  2. triangle.sideB = 4;
  3. triangle.sideC = 5;

객체에 새로운 프로퍼티를 생성하기 위해서 다른 특별한 일을 할 필요는 없다. 자바스크립트가 점 연산자를 평가할 때, 이것은 대단히 관대한 방법을 사용한다. 존재하지 않는 프로퍼티에 접근하려고 시도한다면, 자바스크립트는 그것을 새로 만들어낸다. 존재하지 않는 프로퍼티를 읽으려 한다면 자바스크립트는 “undefined” 값을 반환한다. 이것은 편리하지만, 주의깊게 사용하지 않는다면 에러를 만들어 낸다. 따라서 오타에 주의하도록 하자.

메서드를 추가하는 것 역시 비슷하다 – 예제를 보자:

  1. triangle.getArea = function ( a, b, c ) {
  2. // Return the area of a triangle using Heron’s formula
  3. var semiperimeter = (a + b + c) / 2;
  4. var calculation = semiperimeter * (semiperimeter – a) *
  5. (semiperimeter – b) * (semiperimeter – c);
  6. return Math.sqrt( calculation );
  7. }; // 이 줄에 사용된 세미콜론을 눈여겨 보기 바란다. 이것은 규칙의 일부이다.

이것이 함수 선언과 무척 비슷하다고 생각한다면, 제대로 보고 있는 것이다. 그저 함수 이름이 빠졌을 뿐이다. 자바스크립트는 익명 함수라는 개념을 갖고 있는데, 함수는 이름을 가지는 대신 다른 값들과 마찬가지로 변수에 저장된다. 이 코드에서, 나는 익명 함수를 만들어내고 그것을 triangle 객체의 getArea 프로퍼티에 저장했다. 이제 객체는 데이터와 함께 함수를 데리고 다닌다.

자가 참조

삼각형 객체를 만들어 낸 목적 중 하나는, 삼각형의 데이터와, 그 데이터를 가지고 할 계산 사이의 관계를 만들어 내는 일이었는데, 아직 그것을 완성하지 못했다. triangle.getArea 메서드가 여전히 각 변의 길이를 필요로 하고 있음을 알 수 있을 것이다. 따라서 이런 식으로 명령해야 하는데:

  1. triangle.getArea( triangle.sideA, triangle.sideB, triangle.sideC );

이것이 처음에 했던 코드보다는 나은데, 왜냐하면 이것은 데이터와 계산 사이의 관계를 명확하게 표현하고 있기 때문이다. 하지만, 그 관계는, 메서드에게 어떤 값을 가지고 계산할지를 매번 알려줄 필요가 없는 것이어야 한다. 메서드는 자신을 포함하고 있는 객체 안에서 데이터를 수집할 수 있어야 하고, 사용자에게 값을 입력할 것을 요구하지 않고 그 데이터를 사용해야 한다.

메서드가 자신을 포함한 객체 안에서 데이터를 수집할 수 있게 하는 비결은 this 키워드 안에 있다. 메서드를 정의할 때 이 키워드를 사용함으로서, 메서드가 실행될 때 같은 객체 내의 다른 프로퍼티나 메서드를 참조하도록 지정할 수 있다. this 를 사용하도록 getArea 를 다시 수정하면 다음과 같다:

  1. triangle.getArea = function () {
  2. // Return the area of a triangle using Heron’s formula
  3. var semiperimeter = (this.sideA + this.sideB + this.sideC) / 2;
  4. var calculation = semiperimeter * (semiperimeter – this.sideA) * (semiperimeter – this.sideB) * (semiperimeter – this.sideC);
  5. return Math.sqrt( calculation );
  6. }; // Note the semi-colon here, it’s mandatory.

여기서 보는 바와 같이, this 키워드는 거울과 비슷하게 동작한다. getArea 메서드가 실행될 때, 메서드는 객체 내부에서 sideA, sideB, sideC 프로퍼티를 검색하게 된다. 객체 내부에 프로퍼티가 이미 정의되어 있으므로, 외부에서 입력하는 값에 의존하지 않고 계산을 할 수 있다.

이것은 조금 과하게 단순화시킨 표현이다. this 키워드가 언제나 메서드를 포함하는 객체를 참조하는 것은 아니다 – 사실 이 키워드는 자신을 호출하는 문맥에 기반해서 동작한다. 모호함에 대해 사과드린다. 하지만, 이것을 다루는 것은 이 글의 초점을 벗어나는 일이 될 것이다. 아무튼, 이 글에서는, this 키워드는 항상 triangle 객체를 참조할 것이다.

연관된 배열로서의 객체

객체의 프로퍼티와 메서드에 접근하는 방법에는 점 연산자만이 있는것은 아니다. 이미 다루었던 글, 배열에 대하여를 통해서 충분히 익숙해져 있을, 대괄호 표기법script notation을 사용해서 더 효율적으로 접근할 수 있다. 짧게 말해, 당신은 객체를 연관된 – 일반적인 배열이, 값에 숫자를 할당하는 것과는 대조적으로 값에 문자열을 할당하는 – 배열이라고 생각해도 무방하다는 것이다. 이러한 표기법을 사용해서, triangle 객체를 다른 방법으로 작성할 수 있다:

  1. var triangle = new Object();
  2. triangle[‘sideA’] = 3;
  3. triangle[‘sideB’] = 4;
  4. triangle[‘sideC’] = 5;
  5. triangle[‘getArea’] = function ( a, b, c ) {
  6. // Return the area of a triangle using Heron’s formula
  7. var semiperimeter = (a + b + c) / 2;
  8. var calculation = semiperimeter * (semiperimeter – a) * (semiperimeter – b) * (semiperimeter – c);
  9. return Math.sqrt( calculation );
  10. }; // Note the semi-colon here, it’s mandatory.

언뜻 보기에는, 이것은 불필요하게 길게 쓴 것으로 보일 수 있다. 왜 간단한 점 연산자를 사용하지 않는가? 이러한 새로운 문법의 장점은, 프로퍼티의 이름이 프로그램 안에서 완전하게 정의될 필요가 없다는 것이다. 프로퍼티의 이름을 명시하기 위해 변수를 사용할 수 있고, 이러한 것을 허용함으로서 프로그램은 참으로 유연해질 수 있다 – 문맥에 기초해서 다양한 동작을 수행할 수 있다는 뜻이다. 예를 들어, 두개의 객체가 하나의 프로퍼티를 공유하는지 비교해 볼 수 있는 함수를 작성할 수 있다:

  1. function isPropertyShared( objectA, objectB, propertyName ) {
  2. if (
  3. typeof objectA[ propertyName ] !== undefined
  4. &&
  5. typeof objectB[ propertyName ] !== undefined
  6. ) {
  7. alert(“Both objects have a property named ” + propertyName + “!”);
  8. }
  9. }

이러한 함수는, 점 표기법을 사용해서는 절대 만들어 낼 수 없는데, 점 연산자 뒤에는 프로퍼티의 이름을 명시적으로 적어주어야만 하기 때문이다. 당신은 이 문법을 아주 많이 사용하게 될 것이다.

참고 : 연관된 배열은 Perl 언어에서는 해쉬 라고 부르며, C# 에서는 해쉬테이블, C++ 에서는 맵, 자바 에서는 해쉬맵, Python 언어에서는 딕셔너리, 이렇게 다양하게 표현된다. 이것은 아주 강력하면서도 기초적인 프로그래밍 기법이고, 이것의 다른 이름을 이미 알고 있었을 수도 있다.

객체 리터럴

아주 친숙한 코드를 좀 더 자세히 들여다보도록 하자:

alert(“Hello world”);

alert는 함수이고, “Hello world” 라는 문자열을 인수로 가지고 호출되고 있다는 것을 알 수 있다. 여기에서 강조하고자 하는 것은, 이렇게 쓸 필요가 없다는 것인데:

  1. var temporaryString = “Hello world”;
  2. alert(temporaryString);

자바스크립트는, 따옴표(” ”)로 둘러싸인 모든 것은 문자열로 취급되어야 한다고 이해하며, 그것이 어디에 쓰였든 간에 올바르게 처리할 수 있는 필요한 모든 일을 백그라운드로 처리한다. 문자열이 만들어지고, 그것이 함수의 인수로 전달되는 과정이 한번에 이루어졌다. 정형적으로는, “Hello world” 이것은 문자열 리터럴 을 참조한다: 문자열을 생성하기 위해 필요한 모든 것을 완전하게 입력한 것이다.

자바스크립트는, “객체 리터럴” 에 대해서도 비슷한 문법을 가지고 있으며 이것을 통해 문법적인 제약에 지나치게 구애받지 않고 객체를 생성할 수 있다. 객체 리터럴을 가지고, 위에서 만들어 낸 객체를 다시 작성해 보자:

  1. var triangle = {
  2. sideA: 3,
  3. sideB: 4,
  4. sideC: 5,
  5. getArea: function ( a, b, c ) {
  6. // Return the area of a triangle using Heron’s formula
  7. var semiperimeter = (a + b + c) / 2;
  8. var calculation = semiperimeter * (semiperimeter – a) * (semiperimeter – b) * (semiperimeter – c);
  9. return Math.sqrt( calculation );
  10. }
  11. };

역주 : 이러한 표기법을 JavaScript Object Notation – 자바스크립트 객체 표기법 – JSON 이라고 부른다.

이러한 문법은 명확하다: 객체 리터럴은 객체의 시작과 끝을 중괄호로 둘러싸며, 중괄호 안에는 프로퍼티 이름 : 프로퍼티 값 쌍이 콤마로 구분되어 나열된다. 이러한 문법을 사용함으로서, 프로그램의 매 행마다 객체 이름을 반복하는 지루한 작업에서 해방되어 쉽게 프로그램을 구조화할 수 있다.

한가지 조심할 것이 있다 : 객체 리터럴의 프로퍼티 리스트 중 마지막 것(여기에서는, getArea) 뒤에 쉼표를 넣는 실수를 아주 흔하게 범한다. 쉼표는 프로퍼티 사이 에만 넣어야 한다 – 마지막에 넣어진 필요없는 쉼표는 에러를 만든다. 특히, 코드를 이후에 수정하면서 프로퍼티를 추가하거나 지울 때 이런 실수를 범하기 쉬우므로, 쉼표가 정확한 위치에 있도록 주의를 기울일 필요가 있다.

요약 – 배울 것이 더 많이 있다

이 글은, 자바스크립트에서 객체가 갖는 가능성과 한계에 대해서 정말로 수박 겉핧기 정도밖에는 되지 않는다. 이 글을 완전히 읽었다면, 스스로 객체를 생성하여 프로퍼티와 메서드를 추가하고 그것을 자가참조의 형태로 사용하는데에는 익숙해 질 것이다. 이것 외에도 정말로 많은 것들이 있지만, 그중 어떤것도 필수적 이지는 않다. 이 글은 긴 여행의 출발점으로서 기획되었으며, 이후 당신이 길을 헤쳐나가는데 필요로 할 도구를 제공하는 목적으로 작성되었다.

읽어볼 것들

연습문제

  • 객체의 프로퍼티를 참조하기 위해 점 표기법 대신 대괄호 표기법을 사용해야 할 때는 어떤 경우인가?
  • 객체는 어떻게 자신을 참조하는가? 왜 이것이 중요한가?
  • 객체 리터럴이란 무엇인가? 객체 리터럴을 생성할때, 쉼표를 어디에 사용해야 하는가?
  • 삼각형을 표현하고, 넓이를 계산하는 객체를 만들었다. 직사각형에 대해서도 그렇게 해 보기를 바란다. this 키워드를 사용해서, 직사각형의 getArea 메서드가 그 데이터를 필요하지 않은 곳에 전달하는 것을 막도록 해 보라.
소개

이전 글 에서 함수의 컨셉에 대해 소개했다. 함수를 사용함으로서 프로그램들을 논리적인 덩어리들로 쪼개서 당신의 코드를 좀더 잘 조직화하고, 쉽게 재사용할 수 있었다. 이제 당신이 자바스크립트 프로그래밍의 핵심적인 개념들을 거부감 없이 받아들일 수 있게 되었으므로, 객체 에 대해 소개함으로서 활용의 폭을 더욱 넓혀보고자 한다. 객체를 사용함으로서 함수로 정의한 기능성들을 하나로 묶을 수 있고, 또한 그것을 이곳저곳으로 전달하며 또한 참조할 수 있다. 이러한 능력은 당신이 앞으로 작성할 코드에 상당히 실질적인 의미를 갖게 될 것이다 – 비록 지금 이 순간에는 다소 모호하게 들리겠지만 말이다.

눈치채지 못했을수도 있지만, 당신은 이 시리즈를 따라 오면서 암묵적으로 객체들을 사용해 왔다. 이제 이것에 대해 좀 더 명시적인 설명을 함으로서 자바스크립트에서 객체가 어떻게 동작하는지, 그리고 객체를 통해 당신의 프로그램을 좀 더 명확하게 하고 재사용하기 쉽도록 하는 방법을 알려주겠다.

이 글의 구조는 다음과 같다:

  • 왜 객체를 사용하는가?
  • 익숙한 영역
  • 객체 생성하기
  • 자가 참조
  • 연관 배열로서의 객체
  • 객체 리터럴
  • 요약 – 배울것이 더 많이 있다
  • 읽어볼것들
  • 연습문제
다운로드하거나 직접 실행해볼 수 있는 예제가 준비되어 있다. 이것은 삼각형의 면적을 계산하는 코드를 포함하고 있는데, 객체를 사용한 것과 사용하지 않은 것이 있다. 이 코드는 아래에서 설명하는 예제들로 만들어져 있다. 삼각형 객체 예제를 실행해보라.

왜 객체를 사용하는가?

객체에 대해 주의를 기울이는 단 하나 가장 중요한 이유는, 객체를 사용함으로서 당신의 코드에서 데이터와 처리과정을 더 잘 표현할 수 있기 때문이다. 익숙한 예제로서, 당신이 삼각형에 관한 뭔가를 코딩한다고 가정하자. 삼각형은 3개의 변 을 가지고 있다는 것을 알고 있으므로, 특정한 삼각형에 대해 다루려면 당연히 3개의 변수를 만들어야 할 것이다:

  1. // This is a triangle.
  2. var sideA = 3;
  3. var sideB = 4;
  4. var sideC = 5;

자, 삼각형이 준비되었다. 하지만 아직 명확하지는 않다. 따로따로 추적해야 할 세개의 변수를 이제 만들었고, 당신이 이것을 가지고 무엇을 어떻게 하려고 했었나를 떠올리게 해 줄 주석이 있다. 이것은 그다지 명확하거나, 사용할만하다고 할 수는 없다. 어쨌든, 계속해보자. 이 “삼각형”에 대해 어떤 계산을 할 수 있을까? 면적을 알기 위해서, 다음과 같은 함수를 사용할 수 있을 것이다:

  1. function getArea( a, b, c ) {
  2. // Heron의 공식을 이용해서 삼각형의 면적을 계산한다.
  3. var semiperimeter = (a + b + c) / 2;
  4. var calculation = semiperimeter * (semiperimeter – a) * (semiperimeter – b) * (semiperimeter – c);
  5. return Math.sqrt( calculation );
  6. }
  7. alert( getArea( sideA, sideB, sideC ) );

뭔가 계산을 하기 위해서는 삼각형에 대한 모든 정보를 전달해주어야 한다는 것을 깨달았을 것이다. 삼각형과 관련된 활동(계산)은 삼각형의 데이터로부터 완전히 분리되어 있는데, 이러한 고립에는 그다지 의미가 있어 보이지 않는다.

좀 더 보자. 나는 이 함수와 변수들에 대해 상당히 범용적인 이름을 정해주었다: getArea, sideA 와 같이 말이다. 다음주에, 직사각형에 대해서 동작하도록 프로그램을 확장해야 함을 깨달았다면 어떻게 할 것인가? 아마도, 사각형에 사용하기 위해 sideA 와 sideB 같은 변수를 사용하려고 하겠지만, 이러한 변수는 이미 사용되었다. side1 과 side2 를 사용할수도 있겠지만, 이런 변수를 사용함으로서 혼동과 재난을 초래할 것이 분명함을, 당신도 이미 짐작할 것이라고 생각한다. 아마도, rectangleSideA, rectangleSideB 같은 변수들을 시도해 보다가 모순 속에 빠져들게 될 것 같다. 삼각형에 대해 이미 만들어 둔 코드를 다시 수정해서 triangleSideA 같은 것을 쓰게끔 하다가, 필경 에러를 만들어 낼 것이다. 이러한 수작업과 오류는 물론 함수의 이름에도 계속된다 – 두개의 다각형에 대해서, 그 계산이 개념적으로는 같은 것이기 때문에, 똑같이 getArea 라는 이름을 사용하고 싶지만, 그렇게 할 수 없다. 데이터를 표현할 수 있는 더 좋은 방법이 있을것이다.

몇개의 명령어들을 묶어서 단일한, 적당한 이름을 가진 활동(함수)을 만들어 내는 것이 이치에 맞는다. 마찬가지로, 몇가지 “것” 들을 하나의 단위로 묶어서 객체를 만들어내는 것이 이치에 맞는다. 자바스크립트에서 미리 정의된 기초적 데이터 타입(문자열, 상수, 불린 등)에 제한받는 대신, 객체를 만들어 사용함으로서 당신만의 데이터 타입 연결을 만들어 낼 수 있다. 객체에 사용되는 변수들은 숫자나 타입의 제한을 받지 않는다. 이렇게, 형식에 얽매이지 않는 유연함은, 프로그램을 작성할 때 당신이 다루려고 하는 것을 직접적으로 다루는 구조를 만들어낼 수 있게 하며, 또한 그것들을 자바스크립트에 내장된 기초적인것들과 마찬가지로 사용할 수 있게 해준다. 이제 삼각형과 직사각형 객체를 만들 것인데, 각각의 객체는 그 모양을 다루기 위해 필요한 모든 데이터들을 포함할 것이며, 또한 그 데이터들을 가지고 수행하려고 하는 모든 행동들도 함께 포함할 것이다. 이러한 목적을 염두에 두고, 문법을 보도록 하자.

익숙한 영역

이전 글의 마지막 실습을 보면, 다음과 같은 코드가 포함되어 있다:

  1. var obj = document.getElementById( elementID );

그리고:

  1. obj.style.background = ‘rgb(’+red+’,’+green+’,’+blue’)’;

자, 객체가 무엇인지 몰랐지만 이미 객체를 사용하고 있었다. 이 두개의 조각을 좀 더 자세히 분석해서 자바스크립트의 객체 문법에 대해 살펴보도록 하자.

var obj = document.getElementById( elementID ) 이 코드는 익숙해 보인다. 명령어 마지막에 포함되어 있는 괄호는 뭔가가 실행될 것이라는 의미이고, 이 실행의 결과가 obj 라는 변수에 저장될 것이라고 짐작할 수 있다. 여기에서 새로운 것은 딱 하나, 중간에 있는 점 이다. 이러한 점 표기법은 자바스크립트가 객체 안에 저장된 데이터에 접근하기 위해 사용하는 방법이다. 점 은 + 나 – 와 마찬가지로, 피연산자 사이에 있는 연산자이다.

관습적으로, 객체 안에 저장되고 점 연산자를 통해 접근하는 데이터를 property 라고 부른다. 프로퍼티는 함수가 될 수 있는데, 이 경우 method 라 부른다. 이러한 단어에 특별한 의미는 없다 – 메서드는 함수일 뿐이고, 프로퍼티는 변수일 뿐이다.

점 연산자의 왼쪽에는 객체가, 오른쪽에는 프로퍼티가 놓인다. 위의 코드에 이러한 규칙을 적용해 본다면, 자바스크립트에 내장되어 있는 document 객체의 getElementById 메서드에 접근한다고 말할 수 있을 것이다. 이것에 관해 더 자세한 내용은 이어질 DOM 여행 글에서 확인할 수 있다.

다음 코드는 조금 더 흥미롭다: 이것은 점을 두개 가지고 있다. 자바스크립트의 객체 지원 중에서 정말로 흥미로운 것 중 하나는, 점 연산자 체인을 통해 복잡한 구조 속으로 들어간다는 것이다. 짧게 말해서, 이러한 체인은 마치 var x = 2 + 3 + 4 + 5; 를 계산하고 값으로 14를 기대하는 것과 같다. 객체 참조는 자신을 반환하고, 이러한 체인이 왼쪽에서 오른쪽으로 계속 이어진다(친구들에게 이것을 설명하면서 자바스크립트가 점 연산자를 “좌측 연결 삽입 연산자” 로 사용한다고 표현해서 깊은 인상을 줄 수 있을 것이다) 이 경우, obj.style 이 먼저 평가되고, 이것을 해석하면 그것의 background 프로퍼티에 접근하는 객체가 된다. 당신이 원한다면, 괄호를 사용해서 이것을 좀 더 명시적으로 표현할 수 있다: (obj.style).background.

객체 생성

삼각형 객체를 만들기 위해, 다음의 문법을 사용해서 명시적으로 생성할 것이다:

  1. var triangle = new Object();

triangle 는 이제 비어있는 기초이며 무언가를 그 위에 쌓아올려야 한다. 점 연산자를 통해 프로퍼티들을 추가함으로서 그렇게 할 수 있다:

  1. triangle.sideA = 3;
  2. triangle.sideB = 4;
  3. triangle.sideC = 5;

객체에 새로운 프로퍼티를 생성하기 위해서 다른 특별한 일을 할 필요는 없다. 자바스크립트가 점 연산자를 평가할 때, 이것은 대단히 관대한 방법을 사용한다. 존재하지 않는 프로퍼티에 접근하려고 시도한다면, 자바스크립트는 그것을 새로 만들어낸다. 존재하지 않는 프로퍼티를 읽으려 한다면 자바스크립트는 “undefined” 값을 반환한다. 이것은 편리하지만, 주의깊게 사용하지 않는다면 에러를 만들어 낸다. 따라서 오타에 주의하도록 하자.

메서드를 추가하는 것 역시 비슷하다 – 예제를 보자:

  1. triangle.getArea = function ( a, b, c ) {
  2. // Return the area of a triangle using Heron’s formula
  3. var semiperimeter = (a + b + c) / 2;
  4. var calculation = semiperimeter * (semiperimeter – a) *
  5. (semiperimeter – b) * (semiperimeter – c);
  6. return Math.sqrt( calculation );
  7. }; // 이 줄에 사용된 세미콜론을 눈여겨 보기 바란다. 이것은 규칙의 일부이다.

이것이 함수 선언과 무척 비슷하다고 생각한다면, 제대로 보고 있는 것이다. 그저 함수 이름이 빠졌을 뿐이다. 자바스크립트는 익명 함수라는 개념을 갖고 있는데, 함수는 이름을 가지는 대신 다른 값들과 마찬가지로 변수에 저장된다. 이 코드에서, 나는 익명 함수를 만들어내고 그것을 triangle 객체의 getArea 프로퍼티에 저장했다. 이제 객체는 데이터와 함께 함수를 데리고 다닌다.

자가 참조

삼각형 객체를 만들어 낸 목적 중 하나는, 삼각형의 데이터와, 그 데이터를 가지고 할 계산 사이의 관계를 만들어 내는 일이었는데, 아직 그것을 완성하지 못했다. triangle.getArea 메서드가 여전히 각 변의 길이를 필요로 하고 있음을 알 수 있을 것이다. 따라서 이런 식으로 명령해야 하는데:

  1. triangle.getArea( triangle.sideA, triangle.sideB, triangle.sideC );

이것이 처음에 했던 코드보다는 나은데, 왜냐하면 이것은 데이터와 계산 사이의 관계를 명확하게 표현하고 있기 때문이다. 하지만, 그 관계는, 메서드에게 어떤 값을 가지고 계산할지를 매번 알려줄 필요가 없는 것이어야 한다. 메서드는 자신을 포함하고 있는 객체 안에서 데이터를 수집할 수 있어야 하고, 사용자에게 값을 입력할 것을 요구하지 않고 그 데이터를 사용해야 한다.

메서드가 자신을 포함한 객체 안에서 데이터를 수집할 수 있게 하는 비결은 this 키워드 안에 있다. 메서드를 정의할 때 이 키워드를 사용함으로서, 메서드가 실행될 때 같은 객체 내의 다른 프로퍼티나 메서드를 참조하도록 지정할 수 있다. this 를 사용하도록 getArea 를 다시 수정하면 다음과 같다:

  1. triangle.getArea = function () {
  2. // Return the area of a triangle using Heron’s formula
  3. var semiperimeter = (this.sideA + this.sideB + this.sideC) / 2;
  4. var calculation = semiperimeter * (semiperimeter – this.sideA) * (semiperimeter – this.sideB) * (semiperimeter – this.sideC);
  5. return Math.sqrt( calculation );
  6. }; // Note the semi-colon here, it’s mandatory.

여기서 보는 바와 같이, this 키워드는 거울과 비슷하게 동작한다. getArea 메서드가 실행될 때, 메서드는 객체 내부에서 sideA, sideB, sideC 프로퍼티를 검색하게 된다. 객체 내부에 프로퍼티가 이미 정의되어 있으므로, 외부에서 입력하는 값에 의존하지 않고 계산을 할 수 있다.

이것은 조금 과하게 단순화시킨 표현이다. this 키워드가 언제나 메서드를 포함하는 객체를 참조하는 것은 아니다 – 사실 이 키워드는 자신을 호출하는 문맥에 기반해서 동작한다. 모호함에 대해 사과드린다. 하지만, 이것을 다루는 것은 이 글의 초점을 벗어나는 일이 될 것이다. 아무튼, 이 글에서는, this 키워드는 항상 triangle 객체를 참조할 것이다.

연관된 배열로서의 객체

객체의 프로퍼티와 메서드에 접근하는 방법에는 점 연산자만이 있는것은 아니다. 이미 다루었던 글, 배열에 대하여를 통해서 충분히 익숙해져 있을, 대괄호 표기법script notation을 사용해서 더 효율적으로 접근할 수 있다. 짧게 말해, 당신은 객체를 연관된 – 일반적인 배열이, 값에 숫자를 할당하는 것과는 대조적으로 값에 문자열을 할당하는 – 배열이라고 생각해도 무방하다는 것이다. 이러한 표기법을 사용해서, triangle 객체를 다른 방법으로 작성할 수 있다:

  1. var triangle = new Object();
  2. triangle[‘sideA’] = 3;
  3. triangle[‘sideB’] = 4;
  4. triangle[‘sideC’] = 5;
  5. triangle[‘getArea’] = function ( a, b, c ) {
  6. // Return the area of a triangle using Heron’s formula
  7. var semiperimeter = (a + b + c) / 2;
  8. var calculation = semiperimeter * (semiperimeter – a) * (semiperimeter – b) * (semiperimeter – c);
  9. return Math.sqrt( calculation );
  10. }; // Note the semi-colon here, it’s mandatory.

언뜻 보기에는, 이것은 불필요하게 길게 쓴 것으로 보일 수 있다. 왜 간단한 점 연산자를 사용하지 않는가? 이러한 새로운 문법의 장점은, 프로퍼티의 이름이 프로그램 안에서 완전하게 정의될 필요가 없다는 것이다. 프로퍼티의 이름을 명시하기 위해 변수를 사용할 수 있고, 이러한 것을 허용함으로서 프로그램은 참으로 유연해질 수 있다 – 문맥에 기초해서 다양한 동작을 수행할 수 있다는 뜻이다. 예를 들어, 두개의 객체가 하나의 프로퍼티를 공유하는지 비교해 볼 수 있는 함수를 작성할 수 있다:

  1. function isPropertyShared( objectA, objectB, propertyName ) {
  2. if (
  3. typeof objectA[ propertyName ] !== undefined
  4. &&
  5. typeof objectB[ propertyName ] !== undefined
  6. ) {
  7. alert(“Both objects have a property named ” + propertyName + “!”);
  8. }
  9. }

이러한 함수는, 점 표기법을 사용해서는 절대 만들어 낼 수 없는데, 점 연산자 뒤에는 프로퍼티의 이름을 명시적으로 적어주어야만 하기 때문이다. 당신은 이 문법을 아주 많이 사용하게 될 것이다.

참고 : 연관된 배열은 Perl 언어에서는 해쉬 라고 부르며, C# 에서는 해쉬테이블, C++ 에서는 맵, 자바 에서는 해쉬맵, Python 언어에서는 딕셔너리, 이렇게 다양하게 표현된다. 이것은 아주 강력하면서도 기초적인 프로그래밍 기법이고, 이것의 다른 이름을 이미 알고 있었을 수도 있다.

객체 리터럴

아주 친숙한 코드를 좀 더 자세히 들여다보도록 하자:

alert(“Hello world”);

alert는 함수이고, “Hello world” 라는 문자열을 인수로 가지고 호출되고 있다는 것을 알 수 있다. 여기에서 강조하고자 하는 것은, 이렇게 쓸 필요가 없다는 것인데:

  1. var temporaryString = “Hello world”;
  2. alert(temporaryString);

자바스크립트는, 따옴표(” ”)로 둘러싸인 모든 것은 문자열로 취급되어야 한다고 이해하며, 그것이 어디에 쓰였든 간에 올바르게 처리할 수 있는 필요한 모든 일을 백그라운드로 처리한다. 문자열이 만들어지고, 그것이 함수의 인수로 전달되는 과정이 한번에 이루어졌다. 정형적으로는, “Hello world” 이것은 문자열 리터럴 을 참조한다: 문자열을 생성하기 위해 필요한 모든 것을 완전하게 입력한 것이다.

자바스크립트는, “객체 리터럴” 에 대해서도 비슷한 문법을 가지고 있으며 이것을 통해 문법적인 제약에 지나치게 구애받지 않고 객체를 생성할 수 있다. 객체 리터럴을 가지고, 위에서 만들어 낸 객체를 다시 작성해 보자:

  1. var triangle = {
  2. sideA: 3,
  3. sideB: 4,
  4. sideC: 5,
  5. getArea: function ( a, b, c ) {
  6. // Return the area of a triangle using Heron’s formula
  7. var semiperimeter = (a + b + c) / 2;
  8. var calculation = semiperimeter * (semiperimeter – a) * (semiperimeter – b) * (semiperimeter – c);
  9. return Math.sqrt( calculation );
  10. }
  11. };

역주 : 이러한 표기법을 JavaScript Object Notation – 자바스크립트 객체 표기법 – JSON 이라고 부른다.

이러한 문법은 명확하다: 객체 리터럴은 객체의 시작과 끝을 중괄호로 둘러싸며, 중괄호 안에는 프로퍼티 이름 : 프로퍼티 값 쌍이 콤마로 구분되어 나열된다. 이러한 문법을 사용함으로서, 프로그램의 매 행마다 객체 이름을 반복하는 지루한 작업에서 해방되어 쉽게 프로그램을 구조화할 수 있다.

한가지 조심할 것이 있다 : 객체 리터럴의 프로퍼티 리스트 중 마지막 것(여기에서는, getArea) 뒤에 쉼표를 넣는 실수를 아주 흔하게 범한다. 쉼표는 프로퍼티 사이 에만 넣어야 한다 – 마지막에 넣어진 필요없는 쉼표는 에러를 만든다. 특히, 코드를 이후에 수정하면서 프로퍼티를 추가하거나 지울 때 이런 실수를 범하기 쉬우므로, 쉼표가 정확한 위치에 있도록 주의를 기울일 필요가 있다.

요약 – 배울 것이 더 많이 있다

이 글은, 자바스크립트에서 객체가 갖는 가능성과 한계에 대해서 정말로 수박 겉핧기 정도밖에는 되지 않는다. 이 글을 완전히 읽었다면, 스스로 객체를 생성하여 프로퍼티와 메서드를 추가하고 그것을 자가참조의 형태로 사용하는데에는 익숙해 질 것이다. 이것 외에도 정말로 많은 것들이 있지만, 그중 어떤것도 필수적 이지는 않다. 이 글은 긴 여행의 출발점으로서 기획되었으며, 이후 당신이 길을 헤쳐나가는데 필요로 할 도구를 제공하는 목적으로 작성되었다.

읽어볼 것들

연습문제

  • 객체의 프로퍼티를 참조하기 위해 점 표기법 대신 대괄호 표기법을 사용해야 할 때는 어떤 경우인가?
  • 객체는 어떻게 자신을 참조하는가? 왜 이것이 중요한가?
  • 객체 리터럴이란 무엇인가? 객체 리터럴을 생성할때, 쉼표를 어디에 사용해야 하는가?
  • 삼각형을 표현하고, 넓이를 계산하는 객체를 만들었다. 직사각형에 대해서도 그렇게 해 보기를 바란다. this 키워드를 사용해서, 직사각형의 getArea 메서드가 그 데이터를 필요하지 않은 곳에 전달하는 것을 막도록 해 보라.

http://www.phpied.com/3-ways-to-define-a-javascript-class/ 에서 참조

Introduction

JavaScript is a very flexible object-oriented language when it comes to syntax. In this article you can find three ways of defining and instantiating an object. Even if you have already picked your favorite way of doing it, it helps to know some alternatives in order to read other people’s code.

It’s important to note that there are no classes in JavaScript. Functions can be used to somewhat simulate classes, but in general JavaScript is a class-less language. Everything is an object. And when it comes to inheritance, objects inherit from objects, not classes from classes as in the “class”-ical languages.

1. Using a function

This is probably one of the most common ways. You define a normal JavaScript function and then create an object by using the new keyword. To define properties and methods for an object created usingfunction(), you use the this keyword, as seen in the following example.

function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = getAppleInfo;
}
 
// anti-pattern! keep reading...
function getAppleInfo() {
    return this.color + ' ' + this.type + ' apple';
}

To instantiate an object using the Apple constructor function, set some properties and call methods you can do the following:

var apple = new Apple('macintosh');
apple.color = "reddish";
alert(apple.getInfo());

1.1. Methods defined internally

In the example above you see that the method getInfo() of the Apple “class” was defined in a separate function getAppleInfo(). While this works fine, it has one drawback – you may end up defining a lot of these functions and they are all in the “global namespece”. This means you may have naming conflicts if you (or another library you are using) decide to create another function with the same name. The way to prevent pollution of the global namespace, you can define your methods within the constructor function, like this:

function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = function() {
        return this.color + ' ' + this.type + ' apple';
    };
}

Using this syntax changes nothing in the way you instantiate the object and use its properties and methods.

1.2. Methods added to the prototype

A drawback of 1.1. is that the method getInfo() is recreated every time you create a new object. Sometimes that may be what you want, but it’s rare. A more inexpensive way is to add getInfo() to the prototype of the constructor function.

function Apple (type) {
    this.type = type;
    this.color = "red";
}
 
Apple.prototype.getInfo = function() {
    return this.color + ' ' + this.type + ' apple';
};

Again, you can use the new objects exactly the same way as in 1. and 1.1.

2. Using object literals

Literals are shorter way to define objects and arrays in JavaScript. To create an empty object using you can do:
var o = {};
instead of the “normal” way:
var o = new Object();
For arrays you can do:
var a = [];
instead of:
var a = new Array();
So you can skip the class-like stuff and create an instance (object) immediately. Here’s the same functionality as described in the previous examples, but using object literal syntax this time:

var apple = {
    type: "macintosh",
    color: "red",
    getInfo: function () {
        return this.color + ' ' + this.type + ' apple';
    }
}

In this case you don’t need to (and cannot) create an instance of the class, it already exists. So you simply start using this instance.

apple.color = "reddish";
alert(apple.getInfo());

Such an object is also sometimes called singleton. It “classical” languages such as Java, singleton means that you can have only one single instance of this class at any time, you cannot create more objects of the same class. In JavaScript (no classes, remember?) this concept makes no sense anymore since all objects are singletons to begin with.

3. Singleton using a function

Again with the singleton, eh? :)

The third way presented in this article is a combination of the other two you already saw. You can use a function to define a singleton object. Here’s the syntax:

var apple = new function() {
    this.type = "macintosh";
    this.color = "red";
    this.getInfo = function () {
        return this.color + ' ' + this.type + ' apple';
    };
}

So you see that this is very similar to 1.1. discussed above, but the way to use the object is exactly like in 2.

apple.color = "reddish";
alert(apple.getInfo());

new function(){...} does two things at the same time: define a function (an anonymous constructor function) and invoke it with new. It might look a bit confusing if you’re not used to it and it’s not too common, but hey, it’s an option, when you really want a constructor function that you’ll use only once and there’s no sense of giving it a name.

Summary

You saw three (plus one) ways of creating objects in JavaScript. Remember that (despite the article’s title) there’s no such thing as a class in JavaScript. Looking forward to start coding using the new knowledge? Happy JavaScript-ing!