/ JavaScript

Private Members in JavaScript

This article is translated in Traditional Chinese.
Article author: Douglas Crockford.
URL: http://javascript.crockford.com/private.html,

JavaScript的私有成員

JavaScript是世界上最被誤解的程式語言。某些人認為它缺少屬性的資訊隱匿性,因為物件無法擁有私有實例變數及方法(method)。但是這是個誤解。JavaScript物件可以擁有私有成員。接下來告訴你為什麼。

Objects 物件

JavaScript物件是基礎的。陣列是物件。函式是物件。Object是物件。所以什麼是物件?物件是鍵值(name-value)的集合。鍵(name)是個字串,值(value)可以是字串、數字、布林值及物件(包含陣列或函式)。物件通常實作成雜湊表,所以值可以被很快的取得。

如果某個值是個函式,我們可以認為它是個方法。當某個物件的方法被實行(invoke),則this變數被設定為該物件。方法可以透過this變數來存取實例變數。

物件可以用constructor來產生,它是個函式並且初始物件。Constructor提供了其他語言在類別所提供的功能,包括靜態變數及方法。

Public 公有

物件的所有成員都是公有成員。任何函式都可以存取、修改或刪除成員,或者新增成員。這裡有兩個主要的方法將成員放到一個新的物件裡。

透過constructor

這技巧通常是用來初始公有實例變數。利用constructor的this變數來增加成員到物件中。

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

所以,當我們建立一個新物件

var myContainer = new Container('abc');

現在myContainer.member包含了'abc'

透過prototype

這個技巧通常用來增加公有方法。當找尋一個成員並且在物件中沒有找到這個成員,那它會從物件中constructor的prototype去拿。Prototype機制是用來繼承。它也會保存記憶體。若要增加某個方法到所有某個constructor所建立的物件中,只要增加一個函式到該constructor的prototype裡即可。

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

所以,我們可以實行這個方法

myContainer.stamp('def')

結果產生'abcdef'

Private 私有

私有變數是透過constructor來產生。平凡的var及constructor的參數成為了私有成員。

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

這個constructor產生了3個私有變數:paramsecretthat。它們被附屬到物件,但是它們無法從外面世界中存取,也無法透過物件本身的公有方法存取。它們只有私有方法可以存取。私有方法則是constructor的內嵌(inline)函式。

function Container(param) {
    function dec() {
        if (secret > 0) {
            secret -= 1;
            return true;
        } else {
            return false;
        }
    }
    this.member = param;
    var secret = 3;
    var that = this;
}

私有方法dec檢查了secret實例變數。如果它大於0,將secret加一並回傳true。其他則回傳false。它可以用來讓這個物件限制對3個變數的使用。

習慣上,我們會產生一個私有that變數。這讓私有變數能夠使用物件。這是一個工作區,用來給ECMAScript Language Specification造成內嵌函式中this被誤設所使用。

私有方法無法被公有方法呼叫。為了讓私有方法更有用,我們需要提出一個特許方法。

Privileged 特許

特許方法有能力存取私有變數及方法,並且它可以被公有方法及外面世界存取。它也可以被刪除或替換,但是它無法被修改或者強迫它放棄它的私密性。

特許方法是在constructor中利用this來指定。

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 () {
        return dec() ? that.member : null;
    };
}

service是一個特許方法。前三次呼叫myContainer.servier()將會回傳'abc'。三次之後,它會回傳nullservice呼叫私有的dec方法,該方法存取私有的secret變數。service可以被其他物件或方法使用,但是它並不允許透過私有方法直接存取。

Closures 閉包

公有、私有及特許成員的樣式是可能的,因為JavaScript有閉包(closure)。意思是它是個內嵌函式,可以存取外包函式(outer function)的var及參數,甚至是外包函式已經回傳離開了。這是這語言中一個非常有力的特性。目前沒有任何有關 JavaScript 書籍中有提到如何利用它。大多數甚至沒有提到它。

私有及特許成員只能在物件建構中產生。公有成員可以在任何時間中加入。

Patterns 樣式

Public 公有

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

Private 私有

function Constructor(...) {
    var that = this;
    var membername = value;

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

注意: 下列函式敘述句

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

是下列敘述句的簡化版

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

Privileged 特許

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