[译]JavaScript Private 实现

JavaScript是世界上最受争议的一门程序开发语言,因为有些人认为它缺乏用来隐藏信息的私有变量和私有方法。这绝对是一种误解,JavaScript对象也有其自身的私有成员实现,现在就让我们一起来实现吧。

对象(Objects)

JavaScript 是基于对象的。数组(Array)是对象,函数(Function)是对象,对象依然还是对象。那什么是对象呢?在JavaScript中,对象就是一个键/值对的集合,键是字符串类型值,值可以是字符串型,数字型,布尔类型,对象(其中当然包括了数组和函数),通常对象实现为哈希类型这样它就可以快速访问到其中的键值。

如果对象中有一个属性是函数,那么这种属性也称之为方法。当一个对象A中的某个函数fa被调用的时候,this变量指向的是当前对象A,fa方法就可以通过this变量访问对象A中的其他变量值。

在JavaScript中,对象是通过调用构造函数生成的,构造函数是进行对象初始化的一类函数。在其他编程语言中,构造函数提供了包含静态变量和方法的一些类的特性。

公共成员(Public)

在JavaScript中,对象的所有成员都是公共成员,任何一个方法都可以被调用,被修改,甚至被删除,当然你也可以在对象上创建新的方法。总的来说,为一个对象创建成员有两种途径:

方法一:Constructor

这种方法通常被用作提供对象的公共属性变量值,通过对this变量的赋值增加对象的成员。

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

我们可以用以下方式来创建一个新的对象。

var myContainer = new Container('abc');

方法二:Prototype

(在讲这个方法之前,先来再熟悉下Prototype这个属性。

当用new运算符创建了一个新的空对象后,紧接着构造函数会作为这个对象的一个方法来调用并设置对象的原型。一个对象的原型就是它的构造函数的prototype属性值。所有的函数都有一个prototype属性,当这个函数被定义的时候,prototype属性自动创建和初始化。prototype属性的初始化值是一个对象,这个对象只带有一个属性。这个属性名为constructor,它指回到和原型相关联的那个构造函数。添加给这个原型对象的任何属性都会成为被构造函数所初始化的对象的属性。)

这种方法通常被用作提供对象的公共方法。当这个方法被查找时且并不在对象的构造函数中存在,这个就需要到这个对象constructor的prototype属性中查找了。prototype使用了一种继承结构,它也保存在内存中(这句话不理解,不都在内存中的?),通过构造函数可以为所有的对象增加公共方法,通过constructor的prototype属性可以增加函数调用。

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

我们是通过如下方式调用的

myContainer.stamp('def')

私有成员(Private)

在JavaScript中,私有成员在构造函数中创建。通常构造函数中使用var声明的变量和传入参数会成为私有成员。

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

以上这个构造函数创建了3个私有变量:param,secret和that,他们附着于对象上,但是却不可以被外部访问,同时也不可以被对象的公共方法访问,但是他们却可以被定义在构造函数中的私有方法访问到。

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变量值,如果secret大于0,它将secret值减一并返回true,否则它讲返回false,这样就可以限制这个对象只能被使用3次。

通常,我们会声明一个用于私有方法的that私有变量,这缘由ECMAScript语言标准关于内部函数的一个错误,这个错误另this不能对内部函数进行正确设置。

私有方法不可以被公共方法调用,所以必须通过特权方法来调用。

特权成员(Privileged)

一个特权方法可以访问私有变量和私有方法,同时它也可以被公共方法或外部访问。删除或者替换特权方法是允许的,但是却不允许修改它。

特权方法需要在构造函数中通过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是一个特权方法,前3次调用myContainer.service()将返回'abc'(前提是创建了var myContainer = new Container('abc')对象)。再次调用的时候,将返回null。service方法调用了可以访问私有变量secret的私有dec方法,service方法可以被其他的对象和方法调用,但是它不允许通过它来访问其中的私有变量。

闭包

公共成员、私有成员及特权成员的实现都是基于JavaScript的闭包特性。JavaScript闭包允许一个内部函数访问其外部函数的变量和参数,特别是在外部函数返回之后,这绝对是JavaScript语言一个最强大的特性。当前并没有任何书籍深入挖掘这一个特性,大多数书籍甚至没有提到它(十几年过去了,这个特性依旧是JavaScript最强大及最迷人的地方,当然也已经有很多书籍对这个特性进行深入讲解了)。

私有成员和特权成员只能够在构造函数中增加,公共成员却可以在任何时候添加。

模式

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 (...) {...};
}

 

附:

使用闭包的私有属性实现

function makePropoty(o, name, predicate) {
    var value;
    o["get"+name] = function() {
        return value;
    };

    o["set"+name] = function(v) {
        if(predicate && !predicate(v)) {
            throw "set" + name + ": invalid value " + v;
        } else {
            value = v;
        }
    };
}

var o = {};
makePropoty(o, "Name", function(x) {return typeof x === "string";});

o.setName("Frank");
console.log(o.getName());
o.setName("David");
console.log(o.getName());
o.setName(0);

 

原文地址:http://javascript.crockford.com/private.html

posted @ 2013-03-31 20:58  swzhou  阅读(1259)  评论(0)    收藏  举报