在Javascript中创建“私有”属性的这两种方式之间的区别

时间:2015-10-21 19:30:37

标签: javascript

我试图在JavaScript中模拟一个简单的Holder“class”,其中包含一个“私有”属性,该属性包含一些东西以及“公共”getter和setter“方法”来访问该值。

下面提到HolderA所展示的方法,例如here。另一种方法我或多或少通过变异来达到,但我想它也必须被认为是一种成语。我喜欢它,因为它不包含thisprototype内容,看起来非常基本和实用。两者之间有区别吗?

测试代码(我在nodejs下运行)似乎表明这两种方法是相同的,只是在第一种情况下我得到的对象有typeof object而在第二个function

var test = function(o) {
    var initialValueCorrect = (!(typeof o.getX()==='undefined'))&&(o.getX()===0);
    var VALUE_TO_SET = 10;
    o.setX(VALUE_TO_SET);
    var getSetWorks = o.getX()===VALUE_TO_SET;
    var xIsPrivate = (typeof o.x === 'undefined');
    var xHasCorrectValue;
    if (!xIsPrivate)
        xHasCorrectValue = o.x === VALUE_TO_SET;
    return {initialValueCorrect: initialValueCorrect,
            getSetWorks : getSetWorks,
            xIsPrivate: xIsPrivate,
            xHasCorrectValue: xHasCorrectValue};
};

var HolderA = (function() {
    function foo(x) {
        this.getX = function() {
            return x;
        };
        this.setX = function(_x) {
            x = _x;
        };
    };
    return foo;
})();

var createHolderB = (function() {
    var x;
    function foo(_x) {
        x = _x;
        return foo;
    }
    foo.getX = function() {
        return  x;
    };
    foo.setX = function(_x) {
        x = _x;
    };
    return foo;
})();



var objects = [{object: new HolderA(0), name: "approach with constructor-invocation and 'this'"},
               {object: createHolderB(0), name: "approach with normal function invocation and closed variable"}];


for (var i = 0; i<objects.length ; i++) {
    var testResult = test(objects[i].object);
    console.log('['+objects[i].name+']: the object is a: '+(typeof objects[i].object)
                +'\n\n\t\t\t'+JSON.stringify(testResult)+'\n\n\n\n\n');
}

更新

正如Bergi指出我上面的代码中的函数createHolderB是完全错误的,只创建一个单例对象。所以,并不是真正的“构造函数”功能。为此,我现在创建了createHolderC,可用于真正创建具有隐藏私有属性的多个对象,如下所示:

var objectC1 = createHolderC()(0);

现在,HolderAcreateHolderC功能之间是否存在任何实质性差异,还是纯粹风格上的区别?

var createHolderC = function () {
    return (function() {
        var x;
        function foo(_x) {
            x = _x;
            return foo;
        };
        foo.getX = function() {
            return x;
        };
        foo.setX = function(_x) {
            x = _x;
        };
        return foo;
    })();
};

3 个答案:

答案 0 :(得分:1)

createHolderB不会像HolderA那样创建新的持有者。它本质上是一种单身模式。您可能还想将其称为模块。请注意createHolderB() === createHolderB

createHolderC仍然与HolderA不同,因为它返回函数对象,而不是实例。去除不必要的IEFE时,您可能会更好地看到差异:

function HolderA(x) {
    this.getX = function() {
        return x;
    };
    this.setX = function(_x) {
        x = _x;
    };
    // implicit `return this;` when called via `new`
}

function createHolderC() {
    var x;
    function foo(_x) {
        x = _x;
        return foo;
    };
    foo.getX = function() {
        return x;
    };
    foo.setX = function(_x) {
        x = _x;
    };
    return foo;
}

典型的工厂看起来像这样:

function createHolderD(x) {
    var foo = {};
    foo.getX = function() {
        return x;
    };
    foo.setX = function(_x) {
        x = _x;
    };
    return foo;
}

(或者甚至是return {getX: …, setX: …};),与HolderA的唯一区别在于原型继承。

答案 1 :(得分:0)

基本上两者都展示了对private变量的x访问行为。但这两者之间的区别是

  1. 构造函数

    var HolderA = (function() {
      function foo(x) {
        this.getX = function() {
            return x;
        };
        this.setX = function(_x) {
            x = _x;
        };
      };
      return foo;
    })();
    

    这是一个自动执行的函数,它返回构造函数 foo。
    这就是您在创建此类持有人时使用new的原因{object: new HolderA(0)

  2. 功能

    var createHolderB = (function() {
      var x;
      function foo(_x) {
        x = _x;
        return foo;
      }
      foo.getX = function() {
        return  x;
      };
      foo.setX = function(_x) {
        x = _x;
      };
      return foo;
    })();
    

    即使这也是一个自动执行的函数,但在执行时它会创建x变量并返回函数foo,而foo通过闭包来访问x
    您只是通过普通函数调用object: createHolderB(0)创建这种类型的持有者。

答案 2 :(得分:0)

这两种情况在实践中都非常糟糕。

如果您正在制作包含“私有变量”范围的closures(匿名函数),则表示您正在为类的每个实例创建这些函数即可。这会耗费内存和性能。我曾经创建过像这样的类,但是一旦我制作了大量的实例,发现它很难达到性能。

就像在Python中一样,通过某种约定使私有变量变为私有,如_name中的下划线。然后你可以出于调试原因打破它(因为闭包范围中的变量不可访问)。

我就是这样做的:

function MyClass(arg) {
    this._privateVar = arg;
}
MyClass.prototype.getPrivateVar = function() {
  return this._privateVar;
}

你也可以制作getter和setter:

Object.defineProperty(MyClass.prototype, "privateVar", {
    get: function() {
      return this._privateVar;
    }
}

但是不要试图将javascript推入其不适合的事物中,否则你将会付出性能,代码可读性和调试复杂性。也适用于其他语言。