Private Members in JavaScript

Toggle Space Navigation Tree
Space Map

Douglas Crockford
www.crockford.com

JavaScript is the world's most misunderstood programming language. Some believe that it lacks the property of information hiding because objects cannot have private instance variables and methods. But this is a misunderstanding. JavaScript objects can have private members. Here's how.
자바스크립트는 세상에서 가장 오해받고 있는 프로그래밍 언어이다. 어떤 이들은 자바스크립트가 private 인스턴스 변수 및 메서드가 없기 때문에 정보 은닉의 특성이 없다고 생각한다. 이건 오해다. 여기를 보면, 자바스크립트가 어떻게 private 멤버를 가질 수 있는지 알 수 있다.

Objects

JavaScript is fundamentally about objects. Arrays are objects. Functions are objects. Objects are objects. So what are objects? Objects are collections of name-value pairs. The names are strings, and the values are strings, numbers, booleans, and objects (including arrays and functions). Objects are usually implemented as hashtables so values can be retrieved quickly.
자바스크립트는 기본적으로 객체에 관한 것이다. 배열은 객체다. 함수도 객체다. 객체(역자주: 예를 들어, var o = new Object( ); 이런 코드에 있어서 Object)도 객체다. 그럼 일반적인 객체란게 무엇인가? 객체는 이름과 값에 대한 쌍의 모음이라고 볼 수 있다. 여기서 이름은 스트링이 될 것이고, 값은 스트링, 숫자, 불린, 객체(배열과 함수 포함)로 표현될 수 있다. 객체는 주로 해쉬테이블로 구현이 되는데, 이유는 빠르게 조회가 가능하기 때문이다.

If a value is a function, we can consider it a method. When a method of an object is invoked, the this variable is set to the object. The method can then access the instance variables through the this variable.
만약 값이 함수라고 한다면, 우린 그걸 메서드라고 생각할 수 있다. 객체의 메서드가 호출이 되면 this라는 변수가 객체에 할당이 된다. 그 이후로는 메서드가 this라는 변수를 통해 인스턴스 변수에 접근하는 것이 가능하다.

Objects can be produced by constructors, which are functions which initialize objects. Constructors provide the features that classes provide in other languages, including static variables and methods.
객체는 생성자에 의해 생길 수 있는데, 여기서 생성자는 객체를 초기화하는 함수이다. 생성자는 다른 언어에서 클래스가 제공하는 특징을 제공하는데, 이에 정적 변수와 메서드도 포함된다.

Public

The members of an object are all public members. Any function can access, modify, or delete those members, or add new members. There are two main ways of putting members in a new object:
객체의 멤버는 모두 public이다. 어떤 함수도 이러한 멤버에 접근, 수정, 삭제가 가능하고, 새로운 멤버도 추가할 수 있다. 새로 생긴 객체에 멤버를 배치하는데는 두가지 방법이 주로 쓰인다.

In the constructor 생성자를 사용하는 경우

This technique is usually used to initialize public instance variables. The constructor's this variable is used to add members to the object.
이 방법은 주로 public 인스턴스 변수를 초기화할 때 사용된다. 해당 객체에 멤버를 추가하기 위해 생성자의 this 변수를 사용한다.

function Container(param) {
    this.member = param;
}

So, if we construct a new object
아래처럼 새로운 객체를 만들면,

var myContainer = new Container('abc');

then myContainer.member contains 'abc'.
myContainer.member는 'abc'를 갖는다.

In the prototype 프로토타입을 사용하는 경우

This technique is usually used to add public methods. When a member is sought and it isn't found in the object itself, then it is taken from the object's constructor's prototype member. The prototype mechanism is used for inheritance. It also conserves memory. To add a method to all objects made by a constructor, add a function to the constructor's prototype:
이 방법은 public 메서드를 추가할 때 주로 사용된다. 멤버를 찾아봐도 객체 안에서 발견이 안되면, 객체의 생성자 프로토타입 멤버의 것을 택한다(역자주: 좀더 공부를 해봐야 할듯..). 프로토타입은 상속을 위해 사용된다. 또한 프로토타입을 사용함으로써 메모리도 절약한다(역자주: 역시 공부해야 함..). 생성자에 의해 만들어진 모든 객체에 메서드를 추가할려면, 생성자의 프로토타입에 함수를 추가하면 된다.

Container.prototype.stamp = function (string) {
    return this.member + string;
}

So, we can invoke the method
아래처럼 메서드를 호출하면,

myContainer.stamp('def')

which produces 'abcdef'.
member 변수는 'abcdef'를 갖게 된다.

Private

Private members are made by the constructor. Ordinary vars and parameters of the constructor becomes the private members.
private 멤버는 생성자에 의해 만들어진다. 보통의 var와 생성자의 파라메타가 private 멤버가 된다.

function Container(param) {
    this.member = param;
    var secret = 3;
    var that = this;
}

This constructor makes three private instance variables: param, secret, and that. They are attached to the object, but they are not accessible to the outside, nor are they accessible to the object's own public methods. They are accessible to private methods. Private methods are inner functions of the constructor.
위 생성자에서는 3개의 private 인스턴스 변수 param, secret, that을 만들었다. 이것들은 해당 객체와 연결되어 있으나 외부에서, 혹은 객체 자체의 public 메서드에서는 접근이 불가능하다. 오로지 private 메서드에서만 접근이 허용되는데, 이 private 메서드는 생성자의 내부 함수이다.

function Container(param) {

    function dec() {
        if (secret > 0) {
            secret -= 1;
            return true;
        } else {
            return false;
        }
    }

    this.member = param;
    var secret = 3;
    var that = this;
}

The private method dec examines the secret instance variable. If it is greater than zero, it decrements secret and returns true. Otherwise it returns false. It can be used to make this object limited to three uses.
위 private 메서드 dec는 secret 인스턴스 변수를 검사한다. 그래서 0보다 크면 secret 변수값을 하나 줄이고 true를 리턴한다. 0보다 크지 않으면 false를 리턴한다. 이는 이 객체를 세 번만 사용하게끔 한다.

By convention, we make a private that parameter. This is used to make the object available to the private methods. This is a workaround for an error in the ECMAScript Language Specification which causes this to be set incorrectly for inner functions.
관례적으로 that이라는 private 변수(역자주: 원문에는 parameter라고 되어 있으나 variable에 대한 오타가 아닐까 생각한다)를 만든다. 이로써 private 메서드에서 해당 객체를 접근 가능하게 만든다. 이는 this 변수가 내부 함수에서 접근 가능하게 정의한 ECMAScript 언어 명세의 오류로 기인하는 에러에 대한 차선책이라고 볼 수 있다. (역자주: 자바스크립트를 잘 모르고, 이런 어려운 내용을 번역할려니,, 담대해져야겠군..)

Private methods cannot be called by public methods. To make private methods useful, we need to introduce a privileged method.
public 메서드가 private 메서드를 호출할 수 없다. private 메서드를 사용가능하게 말들려면, privileged 메서드란게 필요하다.

Privileged

A privileged method is able to access the private variables and methods, and is itself accessible to the public methods and the outside. It is possible to delete or replace a privileged method, but it is not possible to alter it, or to force it to give up its secrets.

Privileged methods are assigned with this within the constructor.

function Container(param) {

    function dec() {
        if (secret > 0) {
            secret -= 1;
            return true;
        } else {
            return false;
        }
    }

    this.member = param;
    var secret = 3;
    var that = this;

    this.service = function () {
        if (dec()) {
            return that.member;
        } else {
            return null;
        }
    };
}

service is a privileged method. Calling myContainer.service() will return 'abc' the first three times it is called. After that, it will return null. service calls the private dec method which accesses the private secret variable. service is available to other objects and methods, but it does not allow direct access to the private members.

Closures

This pattern of public, private, and privileged members is possible because JavaScript has closures. What this means is that an inner function always has access to the vars and parameters of its outer function, even after the outer function has returned. This is an extremely powerful property of the language. There is no book currently available on JavaScript programming that shows how to exploit it. Most don't even mention it.

Private and privileged members can only be made when an object is constructed. Public members can be added at any time.

Patterns

Public
function Constructor(...) {

    this.membername = value;

}
Constructor.prototype.membername = value;
Private
function Constructor(...) {

    var that = this;
    var membername = value;

    function membername(...) {...}

}

Note: The function statement

function membername(...) {...}

is shorthand for

var membername = function membername(...) {...};
Privileged
function Constructor(...) {

    this.membername = function (...) {...};

}
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.